/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */


#include "stdafx.h"
#include "DConfigGlobals.h"
#include "GUIModule.h"
#include "XInterpreter.h"

///////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

/////////////////////////////////////////////
/////////////////////////////////////////////

#ifndef UNDER_CE
#	define AppPumpMessageReplacement()	AfxGetApp()->PumpMessage()
#else
// Under CE, we are using our own message pump so we can get
// the most recent mouse coordinates, so if the user calles
// gui.mousePos(), they will get something other than [0, 0].
// This might be overkill, but for now it should work.
static int _mouseX = 0;
static int _mouseY = 0;
BOOL AppPumpMessageReplacement()	//BOOL CWinThread::PumpMessage()
{
	CWinApp* app = AfxGetApp();
	BOOL bResult = app->PumpMessage();
	if (bResult) {
		MSG* pMsg = &app->m_msgCur;
		if ((pMsg->message >= WM_MOUSEFIRST) && (pMsg->message <= WM_MOUSELAST)) {
			_mouseX = pMsg->pt.x;
			_mouseY = pMsg->pt.y;
		}
	}
	return bResult;
}
#endif // UNDER_CE

/////////////////////////////////////////////
/////////////////////////////////////////////

bool FrameToFont(VALUE vFrame, CFont& font)
{
	// vFame -> {size:120, name:"Arial", bold:true}
	int size = 100;
	CString fontName = _T("Arial");
	bool bBold = false;
	bool bItalic = false;
	bool bUnderline = false;

	if (vFrame.Type() == stFrame) {
		VALUE v = vFrame[_T("size")];
		if (v.Type() == stInteger) {
			size = v.Integer();
		}

		v = vFrame[_T("name")];
		if (v.Type() == stString) {
			fontName = v.String();
		}

		bBold = vFrame[_T("bold")].Boolean();
		bItalic = vFrame[_T("italic")].Boolean();
		bUnderline = vFrame[_T("underline")].Boolean();
	}
	else if (vFrame.Type() != stNil) {
		return false;
	}

	font.DeleteObject(); // Just in case
	//font.CreatePointFont(size, fontName);

	CDC dc;
	dc.CreateCompatibleDC(NULL);
#ifdef UNDER_CE
#	define MulDiv(x, y, z) x * y / z
#endif
	font.CreateFont(
		-MulDiv(size, dc.GetDeviceCaps(LOGPIXELSY), 720),	// logical height of font
		0, 											// logical average character width
		0,											// angle of escapement
		0,											// base-line orientation angle
		bBold ? FW_BOLD : FW_NORMAL,	// font weight
		bItalic ? TRUE : FALSE,		// italic attribute flag
		bUnderline ? TRUE : FALSE,	// underline attribute flag
		FALSE,										// strikeout attribute flag
		DEFAULT_CHARSET,							// character set identifier
		OUT_DEFAULT_PRECIS,							// output precision
		CLIP_DEFAULT_PRECIS,						// clipping precision
		DEFAULT_QUALITY,								// output quality
		DEFAULT_PITCH | FF_DONTCARE,				// pitch and family
		(LPCTSTR)fontName							// pointer to typeface name string
	);
	return true;
}

//////////////////////////////////////////////////////////////////////
// Dialect Window Class
//////////////////////////////////////////////////////////////////////

#include "QWindow.h"

#define NO_MODAL_PARENT	0xFFFFFFFF

#define XERR(x) pInterp->RuntimeError(_T(x))
#define ERR(x) {XERR(x);return AllocNil();}
#define TERR(x) {pInterp->RuntimeError(x);return AllocNil();}

static LPCTSTR lpzWrongNumArgs = _T("Wrong number of arguments to function");
static LPCTSTR lpzExpectString = _T("Expected string argument to function.");
static LPCTSTR lpzExpectInt = _T("Expected integer argument to function");
static LPCTSTR lpzExpectArray = _T("Expected array argument to function");
static LPCTSTR lpzExpectNum = _T("Expected number argument to function");

#define REQUIRE_ARGS(x)		if (argCount != x) TERR(lpzWrongNumArgs)
#define REQUIRE_STRING(x)	if (args[x].Type() != stString) TERR(lpzExpectString)
#define REQUIRE_INTEGER(x)	if ((args[x].Type() != stInteger)) TERR(lpzExpectInt)
#define REQUIRE_ARRAY(x)	if ((args[x].Type() != stArray)) TERR(lpzExpectArray)
#define REQUIRE_NUMBER(x)	if ((args[x].Type() != stInteger) && (args[x].Type() != stFloat)) TERR(lpzExpectNum)

#define BEGIN_METHOD(QType)					\
	ASSERT_SELF(GUI_MODULE_NAME);			\
	VALUE v = self->Get(_T("#pQ"));			\
	if (!(v.Boolean())) return AllocNil();	\
	QType* pQ = (QType*)v.Void()


static VALUE ExecMethod(VALUE self, CString methodName)
{
	VALUE vResult, vMethod = self.Get(methodName);
	if (vMethod.Type() == stFunction) {
			CInterpreter* pInterp = (CInterpreter*)self.Get(_T("#pInterp")).Integer();
			if (pInterp) {
				pInterp->ExecFuncFromCore(vResult, vMethod, 0, NULL, &self);
				if (vResult.Type() == stUndefined) vResult = AllocNil();
			}
	}
	return vResult;	// stUndefined says no method found to execute
}

static VALUE ExecMethod(VALUE self, CString methodName, VALUE arg)
{
	VALUE vResult, vMethod = self.Get(methodName);
	if (vMethod.Type() == stFunction) {
			CInterpreter* pInterp = (CInterpreter*)self.Get(_T("#pInterp")).Integer();
			if (pInterp) {
				pInterp->ExecFuncFromCore(vResult, vMethod, 1, &arg, &self);
				if (vResult.Type() == stUndefined) vResult = AllocNil();
			}
	}
	return vResult;	// stUndefined says no method found to execute
}

static VALUE ExecMethod(VALUE self, CString methodName, VALUE arg1, VALUE arg2)
{
	VALUE vResult, vMethod = self.Get(methodName);
	if (vMethod.Type() == stFunction) {
			CInterpreter* pInterp = (CInterpreter*)self.Get(_T("#pInterp")).Integer();
			if (pInterp) {
				VALUE args[2];
				args[0] = arg1;
				args[1] = arg2;
				pInterp->ExecFuncFromCore(vResult, vMethod, 2, args, &self);
				if (vResult.Type() == stUndefined) vResult = AllocNil();
			}
	}
	return vResult;	// stUndefined says no method found to execute
}

static void ExtractOption(VALUE vFrame, CString opName, int opBit, int& options)
{
	VALUE v = vFrame[opName];
	if (v.Type() != stUndefined) {
		if (v.Boolean()) options |= opBit;
		else options &= ~opBit;
	}
}

CQWindow* ValueToWindow(VALUE p, CInterpreter* pInterp)
{
	if (p.Type() != stClassInstance) {
		XERR("Expecting a window object in arguments to GUI function.");
		return NULL;
	}
	VALUE v = p.Get(_T("#pQ"));
	if (v.Type() != stVoid) {
		XERR("Expecting a window object in arguments to GUI function.");
		return NULL;
	}
	return (CQWindow*)v.Void();
}

bool ValueToRect(VALUE v, CInterpreter* pInterp, CRect& rc)
{
    #define VTRError {XERR("Illegal RECT argument to GUI function."); return false;}

	if (v.Type() == stArray)
	{
		if (v.Length() != 4) VTRError;
		if ((v[1].Type() != stInteger) && (v[1].Type() != stFloat)) VTRError;
		if ((v[2].Type() != stInteger) && (v[2].Type() != stFloat)) VTRError;
		if ((v[3].Type() != stInteger) && (v[3].Type() != stFloat)) VTRError;
		if ((v[4].Type() != stInteger) && (v[4].Type() != stFloat)) VTRError;
		rc.left = v[1].Integer();
		rc.top = v[2].Integer();
		rc.right = v[3].Integer();
		rc.bottom = v[4].Integer();
		return true;
	}
	else if (v.Type() == stFrame)
	{
		if ((v[_T("left")].Type() != stInteger) && (v[_T("left")].Type() != stFloat)) VTRError;
		if ((v[_T("top")].Type() != stInteger) && (v[_T("top")].Type() != stFloat)) VTRError;
		if ((v[_T("right")].Type() != stInteger) && (v[_T("right")].Type() != stFloat)) VTRError;
		if ((v[_T("bottom")].Type() != stInteger) && (v[_T("bottom")].Type() != stFloat)) VTRError;
		rc.left = v[_T("left")].Integer();
		rc.top = v[_T("top")].Integer();
		rc.bottom = v[_T("bottom")].Integer();
		rc.right = v[_T("right")].Integer();
		return true;
	}
	VTRError;
}

//////////////////////////////////////////////////////////////////////
// Dialect QBitmap Class
//////////////////////////////////////////////////////////////////////

#include "QBitmap.h"

void FreeBitmap(void *pVoid)
{
	delete (CQBitmap*)pVoid;
}

CORE_FUNC(Bitmap__Init__)
{
	// gui.bitmap(dx, dy, <filename>)
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 2) && (argCount != 3)) {
		ERR("gui.bitmap contructor requires 2 or 3 arguments");
	}
	REQUIRE_INTEGER(0);
	REQUIRE_INTEGER(1);
	CQBitmap* pQ = new CQBitmap();
	if (argCount == 3) {
		if (!pQ->LoadFile(args[2].String(), args[0].Integer(), args[1].Integer())) {
			delete pQ;
			ERR("Failed to load bitmap from file");
		}
	}
	self->Set(_T("#pQ"),AllocVoid(pQ, FreeBitmap, _T("bitmap")));
	return VALUE();
}

CORE_FUNC(Bitmap_Width)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQBitmap);
	BITMAP bm;
	pQ->GetBitmap(&bm);
	return bm.bmWidth;
}

CORE_FUNC(Bitmap_Height)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQBitmap);
	BITMAP bm;
	pQ->GetBitmap(&bm);
	return bm.bmHeight;
}

VALUE BitmapClass()
{
	VALUE v = AllocClass(_T("bitmap"), VALUE());
	v.Set(_T("__init__"),AllocFunction(Bitmap__Init__, GUI_MODULE_NAME));
	v.Set(_T("width"),AllocFunction(Bitmap_Width, GUI_MODULE_NAME));
	v.Set(_T("height"),AllocFunction(Bitmap_Height, GUI_MODULE_NAME));
	return v;
}

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Generic properties used by multiple window classes

void FreeTheQ(void* pVoid)
{
	CFrameImpl* pFrame = (CFrameImpl*)pVoid;
	pFrame->__set__(_T("#pQ"), AllocNil());
}

CORE_FUNC(Cant_WriteMe)
{
	ERR("This property is read only");
}


CORE_FUNC(Generic_ReadHwnd)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	return (int)pQ->GetSafeHwnd();
}

CORE_FUNC(Generic_ReadParent)
{
	ASSERT(argCount == 1);	// Read prop always has one
	CClassInstImpl* ci = (CClassInstImpl*)args[0].Integer();
	if (ci == NULL) return AllocNil();
	else {
		VALUE v;
		v.ForceCI(stClassInstance, ci);
		return v;
	}
}

CORE_FUNC(Generic_Destroy)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CWnd);
	pQ->SendMessage(WM_DESTROY);
	return AllocNil();
}

CORE_FUNC(Generic_ReadVisible)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	return pQ->IsWindowVisible() == TRUE;
}

CORE_FUNC(Generic_WriteVisible)
{
	ASSERT(argCount == 2);	// Write prop always has one
	BEGIN_METHOD(CWnd);
	pQ->ShowWindow(args[1].Boolean());
	return AllocNil();
}

CORE_FUNC(Generic_ReadEnabled)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	return pQ->IsWindowEnabled() == TRUE;
}

CORE_FUNC(Generic_WriteEnabled)
{
	ASSERT(argCount == 2);	// Write prop always has one
	BEGIN_METHOD(CWnd);
	pQ->EnableWindow(args[1].Boolean());
	return AllocNil();
}

CORE_FUNC(Generic_ReadMouseCapture)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	return pQ == CWnd::GetCapture();
}

CORE_FUNC(Generic_WriteMouseCapture)
{
	ASSERT(argCount == 2);	// Write prop always has one
	BEGIN_METHOD(CWnd);
	if (args[1].Boolean()) {
		pQ->SetCapture();
	}
	else if (pQ == CWnd::GetCapture()) {
		::ReleaseCapture();
	}
	return AllocNil();
}

CORE_FUNC(Generic_ReadFocused)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	return (bool)(pQ == CWnd::GetFocus());
}

CORE_FUNC(Generic_WriteFocused)
{
	ASSERT(argCount == 2);	// Write prop always has one
	BEGIN_METHOD(CWnd);
	pQ->SetFocus();
	return AllocNil();
}

CORE_FUNC(Generic_ReadText)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	CString result;
	pQ->GetWindowText(result);
	return result;
}

CORE_FUNC(Generic_WriteText)
{
	ASSERT(argCount == 2);	// Write prop always has one
	BEGIN_METHOD(CWnd);
	pQ->SetWindowText(args[1].String());
	return AllocNil();
}

CORE_FUNC(Generic_ReadRect)
{
	ASSERT(argCount == 1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	RECT rc;
	pQ->GetWindowRect(&rc);

	CWnd* pParent = pQ->GetParent();
	if (pParent != NULL) pParent->ScreenToClient(&rc);

	VALUE vArray = AllocArray(4);
	vArray.Set(1, rc.left);
	vArray.Set(2, rc.top);
	vArray.Set(3, rc.right);
	vArray.Set(4, rc.bottom);
	return vArray;
}

CORE_FUNC(Generic_WriteRect)
{
	ASSERT(argCount == 2);	// Write prop always has one
	REQUIRE_ARRAY(1);
	BEGIN_METHOD(CWnd);
	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		pQ->MoveWindow(&rc);
	}
	return AllocNil();
}

// These generic properties don't store any data in m_vPropValue,
// so they can be shared by all classes, so let's save some time
// and memory by allocating one shared version of each.
static VALUE vHwndProp = AllocProperty(
		AllocFunction(Generic_ReadHwnd, GUI_MODULE_NAME),
		AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
		AllocNil());
static VALUE vVisibleProp = AllocProperty(
		AllocFunction(Generic_ReadVisible, GUI_MODULE_NAME),
		AllocFunction(Generic_WriteVisible, GUI_MODULE_NAME),
		AllocNil());
static VALUE vEnabledProp = AllocProperty(
		AllocFunction(Generic_ReadEnabled, GUI_MODULE_NAME),
		AllocFunction(Generic_WriteEnabled, GUI_MODULE_NAME),
		AllocNil());
static VALUE vFocusedProp = AllocProperty(
		AllocFunction(Generic_ReadFocused, GUI_MODULE_NAME),
		AllocFunction(Generic_WriteFocused, GUI_MODULE_NAME),
		AllocNil());
static VALUE vTextProp = AllocProperty(
		AllocFunction(Generic_ReadText, GUI_MODULE_NAME),
		AllocFunction(Generic_WriteText, GUI_MODULE_NAME),
		AllocNil());
static VALUE vRectProp = AllocProperty(
		AllocFunction(Generic_ReadRect, GUI_MODULE_NAME),
		AllocFunction(Generic_WriteRect, GUI_MODULE_NAME),
		AllocNil());
static VALUE vMouseCaptureProp = AllocProperty(
		AllocFunction(Generic_ReadMouseCapture, GUI_MODULE_NAME),
		AllocFunction(Generic_WriteMouseCapture, GUI_MODULE_NAME),
		AllocNil());

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

static long NumberOfOpenWindows = 0;

void FreeWindow(void *pVoid)
{
	delete (CQWindow*)pVoid;
	InterlockedDecrement(&NumberOfOpenWindows);
}

VALUE HandleWindow(CQWindow *pQWindow, int event, VALUE arg)
{
	VALUE vResult, self = pQWindow->GetSelf();
	if (event == QW_DESTROY) {
		self.Set(_T("parent"), AllocNil());	// If modal, has parent
		self.Set(_T("#pQ"),AllocNil());
	}
	else if (event == QW_CLOSE) {
		int mp = self.Get(_T("#modalParent")).Integer();
		if (mp && (mp != NO_MODAL_PARENT)) {
			// Kill the modal loop
			CWnd *pmp = (CWnd*)mp;
			if (::IsWindow(pmp->m_hWnd)) pmp->BringWindowToTop(); // Stop strange flickering
			pQWindow->m_nFlags &= ~WF_MODALLOOP;
			self.Set(_T("#modalParent"), AllocNil());
		}
		pQWindow->m_nFlags &= ~WF_MODALLOOP;
		vResult = ExecMethod(self, _T("onClose"));

	}
	else if (event == QW_MENUSELECT) {
		// arg == menu item selected
		vResult = ExecMethod(self, _T("onMenuSelect"), arg);
	}
	else if (event == QW_RESIZE) {
		vResult = ExecMethod(self, _T("onResize"));
	}
	else if (event == QW_TIMER) {
		// arg == timer ID
		vResult = ExecMethod(self, _T("onTimer"), arg);
	}
	else if (event == QW_VISIBLE) {
		vResult = ExecMethod(self, _T("onVisible"), arg);
	}
	else if (event == QW_HOTKEY) {
		// arg == hotkeyID
		vResult = ExecMethod(self, _T("onHotKey"), arg);
	}
	else if (event == QW_KEYPRESS) {
		// Break out args...
		VALUE vChar = arg[1];
		VALUE vInfo = arg[2];
		vResult = ExecMethod(self, _T("onKeyPress"), vChar, vInfo);
	}
	return vResult;
}

CORE_FUNC(Window__Init__)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 2) && (argCount != 3)) {
		ERR("gui.window constructor requires two or three arguments");
	}

	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		// Default options
		int options = QWS_MINBOX | QWS_MAXBOX | QWS_CLOSEBOX | QWS_RESIZABLE |
			QWS_VISIBLE | QWS_TITLEBAR;
		int exOptions = 0;
		if ((argCount == 3) && (args[2].Type() == stFrame)) {
			ExtractOption(args[2], _T("minbox"), QWS_MINBOX, options);
			ExtractOption(args[2], _T("maxbox"), QWS_MAXBOX, options);
			ExtractOption(args[2], _T("closebox"), QWS_CLOSEBOX, options);
			ExtractOption(args[2], _T("resizable"), QWS_RESIZABLE, options);
			ExtractOption(args[2], _T("visible"), QWS_VISIBLE, options);
			ExtractOption(args[2], _T("titlebar"), QWS_TITLEBAR, options);
			ExtractOption(args[2], _T("topmost"), QWS_EX_TOPMOST, exOptions);
		}

		CQWindow* pQWindow = new CQWindow(args[0].String(), rc, options, exOptions,
			HandleWindow, *self);
		InterlockedIncrement(&NumberOfOpenWindows);
		self->Set(_T("#pQ"), AllocVoid(pQWindow, FreeWindow, _T("window")));
	}
	return VALUE();
}

/*CORE_FUNC(Window_Show)
{
	BEGIN_METHOD(CQWindow);
	pQ->ShowWindow(SW_SHOW);
	return AllocNil();
}

CORE_FUNC(Window_Hide)
{
	BEGIN_METHOD(CQWindow);
	pQ->ShowWindow(SW_HIDE);
	return AllocNil();
}

CORE_FUNC(Window_SetText)
{
	BEGIN_METHOD(CQWindow);
	REQUIRE_ARGS(1);
	pQ->SetWindowText(args[0].String());
	return AllocNil();
}

CORE_FUNC(Window_GetText)
{
	BEGIN_METHOD(CQWindow);
	REQUIRE_ARGS(0);
	CString text;
	pQ->GetWindowText(text);
	return text;
}*/

bool PumpMessages(CInterpreter* pInterp);	// Below
CORE_FUNC(Window_DoModal)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQWindow);

	// Make modal with respect to pWnd
	CWnd* pWnd = NULL;
	VALUE vClassInst = args[0];
	if (vClassInst.Type() == stClassInstance) {
		VALUE vParent = vClassInst[_T("#pQ")];
		if (vParent.Type() == stVoid) {
			pWnd = (CWnd*)vParent.Void();
		}
	}

	pQ->ShowWindow(TRUE);	// Make sure we are visible...

	self->Set(_T("parent"), AllocProperty(
		AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
		AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
		(int)(pWnd ? vClassInst.ClassInstance() : 0)));
	self->Set(_T("#modalParent"), (int)(pWnd ? pWnd : (CWnd*)NO_MODAL_PARENT));
	self->Set(_T("__modalresult__"), AllocNil());
	if (pWnd) pWnd->EnableWindow(FALSE);

	// We absolutely can't allow this function to exit without
	// re-enabling pWnd, hence the try..catch
	_TRY {
		// Start up our modal loop...
		// We need our own message pump if we want pInterp exceptions
		// to be handler properly...
		pQ->m_nFlags |= WF_MODALLOOP;
		while ((pQ->m_nFlags & WF_MODALLOOP) &&
			((pInterp->GetReturnFlag() == RETURN_NOT) ||
			(pInterp->GetReturnFlag() == RETURN_FUNCTION)))
		{
			PumpMessages(pInterp);
		}
	}
	_CATCH_EXCEPTION {
		DWORD code = GET_EXC_CODE;
		LPCTSTR msg = GET_EXC_MESSAGE;
		// If an C++ exception is caught, convert it into a Dialect exception...
		switch (code) {
			case EXCEPTION_SYNTAX:
				pInterp->SyntaxError(msg);
				break;
			case EXCEPTION_RUNTIME:
				pInterp->RuntimeError(msg);
				break;
			case EXCEPTION_INTERPRETER:
				pInterp->InternalError(msg);
				break;
			default:
				pInterp->InternalError(_T("Unkown internal error"));
		}
	}
#ifndef USE_SEH
	catch(...) {
		// C++ exception handling will only catch EException class exceptions above
		// because of the we the macro is written, so we need this catch all handler
		// to avoid NTS (Nasty Temination Syndrom)
		pInterp->InternalError(_T("Unkown internal error"));
	}
#endif

	if (pWnd) {
		//pWnd->BringWindowToTop();
		pWnd->EnableWindow(TRUE);
	}
	return self->Get(_T("__modalresult__"));
}

CORE_FUNC(Window_EndModal)
{
	BEGIN_METHOD(CQWindow);
	// HandleWindow() kills the modal loop before calling onClose
	// If the script has a call to endmodal in it's onClose event,
	// we could potentially have an infinite loop, so the onClose
	// removes the #modalParent reference when executed...
	if (self->Get(_T("#modalParent")).Integer() != 0) {
		pQ->SendMessage(WM_CLOSE);
	}

	if (argCount > 0) {
		self->Set(_T("__modalresult__"), args[0]);
	}
	return AllocNil();
}

CORE_FUNC(Window_Close)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQWindow);
	pQ->SendMessage(WM_CLOSE);
	return AllocNil();
}

CORE_FUNC(Window_ReadClientRect)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CWnd);
	RECT rc;
	pQ->GetClientRect(&rc);

	VALUE vArray = AllocArray(4);
	vArray.Set(1, rc.left);
	vArray.Set(2, rc.top);
	vArray.Set(3, rc.right);
	vArray.Set(4, rc.bottom);
	return vArray;
}

CORE_FUNC(Window_WriteClientRect)
{
	REQUIRE_ARGS(2);	// Write prop always has two
	BEGIN_METHOD(CWnd);
	CRect rc;
	if (!ValueToRect(args[1], pInterp, rc)) return AllocNil();
	RECT wrc, crc;
	pQ->GetWindowRect(&wrc);
	pQ->GetClientRect(&crc);
	int borderSize = (wrc.right - wrc.left - crc.right - crc.left) >> 1;
	int titleBarSize = wrc.bottom - wrc.top - borderSize - crc.bottom - crc.top;
	pQ->MoveWindow(CRect(rc.left - borderSize, rc.top - titleBarSize,
		rc.right + borderSize, rc.bottom + borderSize));
	return AllocNil();
}

CORE_FUNC(Window_SetTimer)
{
	REQUIRE_ARGS(2);	// settimer(ID, milliseconds)
	BEGIN_METHOD(CWnd);
	REQUIRE_INTEGER(0);
	REQUIRE_INTEGER(1);
	return (int)pQ->SetTimer(args[0].Integer(), args[1].Integer(), NULL);
}

CORE_FUNC(Window_KillTimer)
{
	REQUIRE_ARGS(1);	// killtimer(ID)
	BEGIN_METHOD(CWnd);
	return (bool)(pQ->KillTimer(args[0].Integer()) != 0);
}

/*CORE_FUNC(Window_ToTop)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CWnd);
	pQ->SetForegroundWindow();	// Newer versions of Windows don't do what I want here
	return AllocNil();
}*/

CORE_FUNC(Window_SystemHotKey)
{
	// id = wnd.registerHotKey("d", {alt:true, ctrl:true})

	// Apps should use 0x0000 -> 0xBFFF
	// Shared DLLs should use 0xC000 -> 0xFFFF (as returned by GlobalAddAtom())
	static int nextID = 1;
	#define lastID	0xBFFF

	REQUIRE_ARGS(2);
	BEGIN_METHOD(CWnd);

	SHORT vk;
	if (args[0].Type() == stInteger) {
		vk = (SHORT)args[0].Integer();
	}
	else {
		TString s = args[0].String();
		if (s.IsEmpty()) {
			ERR("First arg to systemhotkey() must be a single character string");
		}
		#ifdef __WINNT__
			vk = VkKeyScan(s[0]) & 0x00FF;
		#else
			s.MakeUpper();
			vk = s[0];
		#endif
	}

	int mods = 0;
	if ((argCount > 1) && (args[1].Type() == stFrame)){
		ExtractOption(args[1], _T("alt"), MOD_ALT, mods);
		ExtractOption(args[1], _T("ctrl"), MOD_CONTROL, mods);
		ExtractOption(args[1], _T("shift"), MOD_SHIFT, mods);
		ExtractOption(args[1], _T("win"), MOD_WIN, mods);
	}

	while (nextID <= lastID) {
		if (RegisterHotKey(pQ->GetSafeHwnd(), nextID++, mods, vk)) {
			return nextID - 1;
		}
	}
	nextID = 1;
	return 0; // Failed.  Really should loop up to whatever nextID was...
}

CORE_FUNC(Window_UnregSystemHotKey)
{
	REQUIRE_ARGS(1);
	REQUIRE_INTEGER(0);
	BEGIN_METHOD(CWnd);
	return (bool)(UnregisterHotKey(pQ->GetSafeHwnd(), args[0].Integer()) != 0);
}

VALUE WindowClass()
{
	VALUE windowClass = AllocClass(_T("window"),VALUE());
	windowClass.Set(_T("__init__"),AllocFunction(Window__Init__, GUI_MODULE_NAME));
	//windowClass.Set(_T("__del__"), AllocFunction(Window__Del__, GUI_MODULE_NAME));
	windowClass.Set(_T("close"),AllocFunction(Window_Close, GUI_MODULE_NAME));
	//windowClass.Set(_T("totop"),AllocFunction(Window_ToTop, GUI_MODULE_NAME));
	windowClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	//windowClass.Set(_T("show"),AllocFunction(Window_Show, GUI_MODULE_NAME));
	//windowClass.Set(_T("hide"),AllocFunction(Window_Hide, GUI_MODULE_NAME));
	//windowClass.Set(_T("settext"),AllocFunction(Window_SetText, GUI_MODULE_NAME));
	//windowClass.Set(_T("gettext"),AllocFunction(Window_GetText, GUI_MODULE_NAME));
	windowClass.Set(_T("startModal"),AllocFunction(Window_DoModal, GUI_MODULE_NAME));
	windowClass.Set(_T("endModal"),AllocFunction(Window_EndModal, GUI_MODULE_NAME));
	windowClass.Set(_T("starttimer"),AllocFunction(Window_SetTimer, GUI_MODULE_NAME));
	windowClass.Set(_T("endtimer"),AllocFunction(Window_KillTimer, GUI_MODULE_NAME));
	windowClass.Set(_T("systemhotkey"),  AllocFunction(Window_SystemHotKey, GUI_MODULE_NAME));
	windowClass.Set(_T("unregsystemhotkey"),  AllocFunction(Window_UnregSystemHotKey, GUI_MODULE_NAME));
	windowClass.Set(_T("text"), vTextProp);
	windowClass.Set(_T("visible"), vVisibleProp);
	windowClass.Set(_T("enabled"), vEnabledProp);
	windowClass.Set(_T("focused"), vFocusedProp);
	windowClass.Set(_T("rect"), vRectProp);
	windowClass.Set(_T("hwnd"), vHwndProp);
	windowClass.Set(_T("clientrect"), AllocProperty(
		AllocFunction(Window_ReadClientRect, GUI_MODULE_NAME),
		AllocFunction(Window_WriteClientRect, GUI_MODULE_NAME),
		AllocNil()));
	return windowClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Button Class
//////////////////////////////////////////////////////////////////////

#include "QButton.h"

void FreeButton(void *pVoid)
{
	delete (CQButton*)pVoid;
}

VALUE HandleButton(CQButton *pQButton, int event, VALUE arg)
{
	VALUE self = pQButton->GetSelf();//m_vSelf;
	if (event == QW_DESTROY) {
		self.Set(_T("parent"), AllocNil());
		self.Set(_T("#pQ"), AllocNil());
	}
	else if (event == QW_CLICK)	{
		ExecMethod(self, _T("onClick"));
	}
	return VALUE();
}

CORE_FUNC(Button__Init__)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("gui.button contructor takes 3 or 4 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	REQUIRE_STRING(1);
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		bool bFocusRect = true;
		VALUE vFont;
		CQBitmap bitmap, *pBitmap = NULL;
		int options = QBS_PUSHBUTTON;
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			ExtractOption(args[3], _T("group"), QBS_GROUP, options);
			ExtractOption(args[3], _T("defbutton"), QBS_DEFPUSHBUTTON, options);
			VALUE v = args[3][_T("focusrect")];
			if (v.Type() != stUndefined) {
				bFocusRect = v.Boolean();
			}
			v = args[3][_T("bitmap")];
			if ((v.Type() == stClassInstance) && v.Isa(_T("bitmap"))) {
				pBitmap = (CQBitmap*)v.Get(_T("#pQ")).Void();
			}
			else if ((v.Type() == stString) && bitmap.LoadFile(v.String())) {
				pBitmap = &bitmap;
			}
			else if ((v.Type() == stInteger) && bitmap.LoadBitmap(v.Integer())) {
				pBitmap = &bitmap;
			}
			vFont = args[3][_T("font")];
		}
		CQButton* pQButton = new CQButton(pQWindow,args[1].String(), rc, options,
			bFocusRect, pBitmap, vFont, HandleButton, *self);
		self->Set(_T("#pQ"), AllocVoid(pQButton, FreeButton, _T("button")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

/*CORE_FUNC(Button_SetText)
{
	BEGIN_METHOD(CQButton);
	REQUIRE_ARGS(1);
	pQ->SetWindowText(args[0].String());
	return AllocNil();
}

CORE_FUNC(Button_GetText)
{
	BEGIN_METHOD(CQButton);
	REQUIRE_ARGS(0);
	CString text;
	pQ->GetWindowText(text);
	return text;
}*/

VALUE ButtonClass()
{
	VALUE buttonClass = AllocClass(_T("button"),VALUE());
	buttonClass.Set(_T("__init__"),AllocFunction(Button__Init__,GUI_MODULE_NAME));
	//buttonClass.Set(_T("settext"),AllocFunction(Button_SetText,GUI_MODULE_NAME));
	//buttonClass.Set(_T("gettext"),AllocFunction(Button_GetText,GUI_MODULE_NAME));
	//buttonClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	buttonClass.Set(_T("text"), vTextProp);
	buttonClass.Set(_T("visible"), vVisibleProp);
	buttonClass.Set(_T("enabled"), vEnabledProp);
	buttonClass.Set(_T("focused"), vFocusedProp);
	buttonClass.Set(_T("rect"), vRectProp);
	return buttonClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect CheckBox Class
//////////////////////////////////////////////////////////////////////

// A CheckBox is really a button with the BS_CHECKBOX style
// So we are using it's FreeButton and HandleButton functions
CORE_FUNC(CheckBox__Init__)
{
	// gui.checkbox(parent, text, rc, <optionsFrame>)

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("gui.checkbox contructor takes 3 or 4 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		VALUE vFont;
		int options = QBS_CHECKBOX;
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			ExtractOption(args[3], _T("group"), QBS_GROUP, options);
		}
		CQButton* pQButton = new CQButton(pQWindow,args[1].String(), rc, options,
			true, NULL, vFont, HandleButton, *self);
		self->Set(_T("#pQ"), AllocVoid(pQButton, FreeButton, _T("checkbox")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(CheckBox_WriteCheck)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQButton);
	pQ->SetCheck(args[1].Boolean() ? 1 : 0);
	return AllocNil();
}

CORE_FUNC(CheckBox_ReadCheck)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQButton);
	return (bool)(pQ->GetCheck() == 1);
}

VALUE CheckBoxClass()
{
	VALUE v = AllocClass(_T("checkbox"),VALUE());
	v.Set(_T("__init__"),AllocFunction(CheckBox__Init__,GUI_MODULE_NAME));
	v.Set(_T("checked"), AllocProperty(
		AllocFunction(CheckBox_ReadCheck, GUI_MODULE_NAME),
		AllocFunction(CheckBox_WriteCheck, GUI_MODULE_NAME),
		AllocNil()));
	v.Set(_T("text"), vTextProp);
	v.Set(_T("visible"), vVisibleProp);
	v.Set(_T("enabled"), vEnabledProp);
	v.Set(_T("focused"), vFocusedProp);
	v.Set(_T("rect"), vRectProp);
	return v;
}

//////////////////////////////////////////////////////////////////////
// Dialect RadioButton Class
//////////////////////////////////////////////////////////////////////

// A RadioButton is really a button with the BS_RADIOBUTTON style
// So we are using it's FreeButton and HandleButton functions...

CORE_FUNC(RadioButton__Init__)
{
	// gui.radio(parent, text, rc, <optionsFrame>)

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("gui.radio contructor takes 3 or 4 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		VALUE vFont;
		int options = QBS_RADIOBUTTON;
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			ExtractOption(args[3], _T("group"), QBS_GROUP, options);
		}
		CQButton* pQButton = new CQButton(pQWindow,args[1].String(), rc, options,
			true, NULL, vFont, HandleButton, *self);
		self->Set(_T("#pQ"), AllocVoid(pQButton, FreeButton, _T("radio")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

VALUE RadioButtonClass()
{
	VALUE v = AllocClass(_T("radio"),VALUE());
	v.Set(_T("__init__"),AllocFunction(RadioButton__Init__,GUI_MODULE_NAME));
	// Checked property identical to checkbox version, so use it
	v.Set(_T("checked"), AllocProperty(
		AllocFunction(CheckBox_ReadCheck, GUI_MODULE_NAME),
		AllocFunction(CheckBox_WriteCheck, GUI_MODULE_NAME),
		AllocNil()));
	v.Set(_T("text"), vTextProp);
	v.Set(_T("visible"), vVisibleProp);
	v.Set(_T("enabled"), vEnabledProp);
	v.Set(_T("focused"), vFocusedProp);
	v.Set(_T("rect"), vRectProp);
	return v;
}

//////////////////////////////////////////////////////////////////////
// Dialect Calendar Class
//////////////////////////////////////////////////////////////////////

#include "QCalendar.h"

void FreeCalendar(void *pVoid)
{
	delete (CQCalendar*)pVoid;
}

VALUE HandleCalendar(CQCalendar *pQCalendar, int event, VALUE arg)
{
	VALUE self = pQCalendar->GetSelf();//m_vSelf;
	if (event == QW_DESTROY) {
		self.Set(_T("parent"), AllocNil());
		self.Set(_T("#pQ"), AllocNil());
	}
	else if (event == QW_SELCHANGE)	{
		ExecMethod(self, _T("onSelChange"));
	}
	return VALUE();
}

CORE_FUNC(Calendar__Init__)
{
	// gui.calendar(parent, date, rect)

	ASSERT_SELF(GUI_MODULE_NAME);
	if (argCount != 3) {
		ERR("gui.calendar contructor takes 3 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		CQCalendar* pQCalendar = new CQCalendar(pQWindow, args[1].String(), rc,
			HandleCalendar, *self);
		self->Set(_T("#pQ"), AllocVoid(pQCalendar, FreeCalendar, _T("calendar")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(Calendar_ShortDate)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQCalendar);
	return pQ->ShortDate();
}

CORE_FUNC(Calendar_LongDate)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQCalendar);
	return pQ->LongDate();
}

CORE_FUNC(Calendar_ReadDate)
{
	// Default to reading in ShortDate() style
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQCalendar);
	return pQ->ShortDate();
}

CORE_FUNC(Calendar_WriteDate)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQCalendar);
	pQ->SetDate(args[1].String());
	return AllocNil();
}

VALUE CalendarClass()
{
	VALUE CalendarClass = AllocClass(_T("calendar"),VALUE());
	CalendarClass.Set(_T("__init__"),AllocFunction(Calendar__Init__,GUI_MODULE_NAME));
	CalendarClass.Set(_T("shortdate"),AllocFunction(Calendar_ShortDate,GUI_MODULE_NAME));
	CalendarClass.Set(_T("longdate"),AllocFunction(Calendar_LongDate,GUI_MODULE_NAME));
	CalendarClass.Set(_T("date"), AllocProperty(
		AllocFunction(Calendar_ReadDate, GUI_MODULE_NAME),
		AllocFunction(Calendar_WriteDate, GUI_MODULE_NAME),
		AllocNil()));
	CalendarClass.Set(_T("visible"), vVisibleProp);
	CalendarClass.Set(_T("enabled"), vEnabledProp);
	CalendarClass.Set(_T("focused"), vFocusedProp);
	CalendarClass.Set(_T("rect"), vRectProp);
	return CalendarClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Pane Class
//////////////////////////////////////////////////////////////////////

#include "QPane.h"

void FreePane(void *pVoid)
{
	delete (CQPane*)pVoid;
}

VALUE HandlePane(CQPane *pQPane, int event, VALUE arg)
{
	VALUE self = pQPane->GetSelf();
	switch (event) {
		case QW_DESTROY:
			self.Set(_T("parent"), AllocNil());
			self.Set(_T("#pQ"), AllocNil());
			break;
		case QW_CLICK:		return ExecMethod(self, _T("onClick"));
		case QW_BUTTONUP:	return ExecMethod(self, _T("onButtonUp"), arg);
		case QW_BUTTONDOWN:	return ExecMethod(self, _T("onButtonDown"), arg);
		case QW_RESIZE:		return ExecMethod(self, _T("onResize"));
		case QW_MOUSEMOVE:	return ExecMethod(self, _T("onMouseMove"));
		case QW_PAINT:		return ExecMethod(self, _T("onPaint"));
		case QW_MENUSELECT: return ExecMethod(self, _T("onMenuSelect"), arg);	// arg == menu item selected
		case QW_VISIBLE:	return ExecMethod(self, _T("onVisible"), arg);		// arg == bVisible
	}
	return VALUE();
}

CORE_FUNC(Pane__Init__)
{
	// gui.pane(parent, rect, <optionsFrame>)

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 2) && (argCount != 3)) {
		ERR("gui.pane contructor takes 2 or 3 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		CQPane::BorderStyle bs = CQPane::bsNone;	// Default
		if ((argCount == 3) && (args[2].Type() == stFrame)) {
			VALUE v = args[2][_T("border")];
			if (v.Type() != stUndefined) {
				CString b = v.String();
				if (b.CompareNoCase(_T("none")) == 0) {
					bs = CQPane::bsNone;
				}
				else if (b.CompareNoCase(_T("line")) == 0) {
					bs = CQPane::bsLine;
				}
				else if (b.CompareNoCase(_T("raised")) == 0) {
					bs = CQPane::bsRaised;
				}
				else if (b.CompareNoCase(_T("sunken")) == 0) {
					bs = CQPane::bsSunken;
				}
			}
		}
		CQPane* pQPane = new CQPane(pQWindow, _T(""), rc, bs, 0, HandlePane, *self);
		self->Set(_T("#pQ"), AllocVoid(pQPane, FreePane, _T("pane")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(Pane_ReadBorder)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQPane);
	switch (pQ->GetBorderStyle()) {
		case CQPane::bsNone:	return AllocSymbol(_T("none"));
		case CQPane::bsLine:	return AllocSymbol(_T("line"));
		case CQPane::bsRaised:	return AllocSymbol(_T("raised"));
		case CQPane::bsSunken:	return AllocSymbol(_T("sunken"));
	}
	return AllocNil();
}

CORE_FUNC(Pane_WriteBorder)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQPane);
	CString style = args[1].String();
	if (style.CompareNoCase(_T("none")) == 0) {
		pQ->SetBorderStyle(CQPane::bsNone);
	}
	else if (style.CompareNoCase(_T("line")) == 0) {
		pQ->SetBorderStyle(CQPane::bsLine);
	}
	else if (style.CompareNoCase(_T("raised")) == 0) {
		pQ->SetBorderStyle(CQPane::bsRaised);
	}
	else if (style.CompareNoCase(_T("sunken")) == 0) {
		pQ->SetBorderStyle(CQPane::bsSunken);
	}
	return AllocNil();
}

CORE_FUNC(Pane_ReadClientRect)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CQPane);
	RECT rc;
	pQ->GetClientRect(&rc);

	VALUE vArray = AllocArray(4);
	vArray.Set(1, rc.left);
	vArray.Set(2, rc.top);
	vArray.Set(3, rc.right);
	vArray.Set(4, rc.bottom);
	return vArray;
}

CORE_FUNC(Pane_Repaint)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQPane);
	pQ->InvalidateRect(NULL);
	return AllocNil();
}

CORE_FUNC(Pane_Line)
{
	REQUIRE_ARGS(4);
	REQUIRE_NUMBER(0);
	REQUIRE_NUMBER(1);
	REQUIRE_NUMBER(2);
	REQUIRE_NUMBER(3);
	BEGIN_METHOD(CQPane);
	pQ->Line(args[0].Integer(), args[1].Integer(), args[2].Integer(), args[3].Integer());
	return AllocNil();
}


CORE_FUNC(Pane_DrawText)
{
	// pane.drawtext(x, y, text)
	REQUIRE_ARGS(3);
	REQUIRE_NUMBER(0);
	REQUIRE_NUMBER(1);
	BEGIN_METHOD(CQPane);
	pQ->DrawText(args[0].Integer(), args[1].Integer(), args[2].String());
	return args[0];
}

/*CORE_FUNC(Pane_DrawPoint)
{
	// pane.drawpoint(x, y, <[r, g, b]>)
	if (argCount < 2) {
		ERR("Invalid arg count");
	}
	REQUIRE_NUMBER(0);
	REQUIRE_NUMBER(1);
	BEGIN_METHOD(CQPane);
	COLORREF color, *pcolor = NULL;
	if ((argCount >= 3) && (args[2].Type() == stArray)) {
		color = RGB(args[2][1].Integer(), args[2][2].Integer(), args[2][3].Integer());
		pcolor = &color;
	}
	pQ->DrawPoint(args[0].Integer(), args[1].Integer(), pcolor);
	return args[0];
}*/

CORE_FUNC(Pane_WritePen)
{
	// pane.pen = {width:3, color:[r, g, b], style:'dash}
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQPane);
	if (args[1].Type() == stNil) {
		pQ->SetPen(1, RGB(0, 0, 0));
	}
	else if (args[1].Type() == stFrame) {
		int width = 1;
		COLORREF color = RGB(0, 0, 0);
		VALUE v = args[1][_T("width")];
		if ((v.Type() == stInteger) || (v.Type() == stFloat)) {
			width = v.Integer();
		}
		v = args[1][_T("color")];
		if (v.Type() == stArray) {
			if ((v.Length() != 3) || (v[1].Type() != stInteger) ||
				(v[2].Type() != stInteger) || (v[3].Type() != stInteger))
			{
				ERR("pen color must be an array of 3 integers: [r, g, b]")
			}
			color = RGB(v[1].Integer(), v[2].Integer(), v[3].Integer());
		}
		pQ->SetPen(width, color);
	}
	else {
		ERR("Invalid type assigned to pen property");
	}
	return AllocNil();
}

CORE_FUNC(Pane_WriteFont)
{
	// pane.font = {size:120, font:"Arial"}
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQPane);
	if (args[1].Type() == stNil) {
		pQ->SetFont(_T("Arial"), 100);
	}
	else if (args[1].Type() == stFrame) {
		int size = 100;
		CString fontName = _T("Arial");

		VALUE v = args[1][_T("size")];
		if (v.Type() == stInteger) {
			size = v.Integer();
		}

		v = args[1][_T("font")];	// For backwards compat, delete eventually
		if (v.Type() == stString) {	//
			fontName = v.String();	//
		}							//
		v = args[1][_T("name")];
		if (v.Type() == stString) {
			fontName = v.String();
		}

		if (!pQ->SetFont(fontName, size)) {
			ERR("Failed to set font");
		}
	}
	else {
		ERR("Invalid type assigned to pen property");
	}
	return AllocNil();
}

VALUE PaneClass()
{
	VALUE PaneClass = AllocClass(_T("pane"),VALUE());
	PaneClass.Set(_T("__init__"),AllocFunction(Pane__Init__, GUI_MODULE_NAME));
	PaneClass.Set(_T("repaint"), AllocFunction(Pane_Repaint, GUI_MODULE_NAME));
	PaneClass.Set(_T("line"), AllocFunction(Pane_Line, GUI_MODULE_NAME));  // Delete me
	PaneClass.Set(_T("drawline"), AllocFunction(Pane_Line, GUI_MODULE_NAME));
	PaneClass.Set(_T("drawtext"), AllocFunction(Pane_DrawText, GUI_MODULE_NAME));
	//PaneClass.Set(_T("drawpoint"), AllocFunction(Pane_DrawPoint, GUI_MODULE_NAME));
	PaneClass.Set(_T("pen"), AllocProperty(
		AllocNil(),
		AllocFunction(Pane_WritePen, GUI_MODULE_NAME),
		AllocNil()));
	PaneClass.Set(_T("font"), AllocProperty(
		AllocNil(),
		AllocFunction(Pane_WriteFont, GUI_MODULE_NAME),
		AllocNil()));
	PaneClass.Set(_T("clientrect"), AllocProperty(
		AllocFunction(Pane_ReadClientRect, GUI_MODULE_NAME),
		AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
		AllocNil()));
	PaneClass.Set(_T("border"), AllocProperty(
		AllocFunction(Pane_ReadBorder, GUI_MODULE_NAME),
		AllocFunction(Pane_WriteBorder, GUI_MODULE_NAME),
		AllocNil()));
	PaneClass.Set(_T("visible"), vVisibleProp);
	PaneClass.Set(_T("enabled"), vEnabledProp);
	PaneClass.Set(_T("focused"), vFocusedProp);
	PaneClass.Set(_T("rect"), vRectProp);
	PaneClass.Set(_T("mousecapture"), vMouseCaptureProp);
	return PaneClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Ink Class
//////////////////////////////////////////////////////////////////////

#ifdef DIALECT_ENT
#include "QInk.h"

void FreeInk(void *pVoid)
{
	delete (CQInk*)pVoid;
}

VALUE HandleInk(CQInk *pQInk, int event, VALUE arg)
{
	VALUE self = pQInk->GetSelf();
	switch (event) {
		case QW_DESTROY:
			self.Set(_T("parent"), AllocNil());
			self.Set(_T("#pQ"), AllocNil());
			return VALUE();
	}
	return VALUE();
}

CORE_FUNC(Ink__Init__)
{
	// gui.ink(parent, rect, initData, <optionsFrame>)

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("gui.ink contructor takes 3 or 4 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		CQInk::BorderStyle bs = CQInk::bsNone;	// Default
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			VALUE v = args[3][_T("border")];
			if (v.Type() != stUndefined) {
				CString b = v.String();
				if (b.CompareNoCase(_T("none")) == 0) bs = CQInk::bsNone;
				else if (b.CompareNoCase(_T("line")) == 0) bs = CQInk::bsLine;
				else if (b.CompareNoCase(_T("raised")) == 0) bs = CQInk::bsRaised;
				else if (b.CompareNoCase(_T("sunken")) == 0) bs = CQInk::bsSunken;
			}
		}
		VALUE vData = args[2];
		if (vData.Type() != stChunk) vData = AllocNil();
		CQInk* pQInk = new CQInk(pQWindow, rc, bs, vData, 0, HandleInk, *self);
		self->Set(_T("#pQ"), AllocVoid(pQInk, FreeInk, _T("ink")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(Ink_ReadBorder)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQInk);
	switch (pQ->GetBorderStyle()) {
		case CQInk::bsNone:	return AllocSymbol(_T("none"));
		case CQInk::bsLine:	return AllocSymbol(_T("line"));
		case CQInk::bsRaised:	return AllocSymbol(_T("raised"));
		case CQInk::bsSunken:	return AllocSymbol(_T("sunken"));
	}
	return AllocNil();
}

CORE_FUNC(Ink_WriteBorder)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQInk);
	CString style = args[1].String();
	if (style.CompareNoCase(_T("none")) == 0) pQ->SetBorderStyle(CQInk::bsNone);
	else if (style.CompareNoCase(_T("line")) == 0) pQ->SetBorderStyle(CQInk::bsLine);
	else if (style.CompareNoCase(_T("raised")) == 0) pQ->SetBorderStyle(CQInk::bsRaised);
	else if (style.CompareNoCase(_T("sunken")) == 0) pQ->SetBorderStyle(CQInk::bsSunken);
	return AllocNil();
}

CORE_FUNC(Ink_ReadData)
{
	BEGIN_METHOD(CQInk);
	return pQ->getData();
}

CORE_FUNC(Ink_WriteData)
{
	BEGIN_METHOD(CQInk);
	VALUE vData = args[1];
	if ((vData.Type() == stNil) || (vData.Type() == stChunk)) {
		pQ->setData(vData);
	}
	else {
		ERR("Invalid data type assigned to ink.data property");
	}
	return AllocNil();
}

VALUE InkClass()
{
	VALUE v = AllocClass(_T("ink"),VALUE());
	v.Set(_T("__init__"),AllocFunction(Ink__Init__, GUI_MODULE_NAME));
	v.Set(_T("border"), AllocProperty(
		AllocFunction(Ink_ReadBorder, GUI_MODULE_NAME),
		AllocFunction(Ink_WriteBorder, GUI_MODULE_NAME),
		AllocNil()));
	v.Set(_T("data"), AllocProperty(
		AllocFunction(Ink_ReadData, GUI_MODULE_NAME),
		AllocFunction(Ink_WriteData, GUI_MODULE_NAME),
		AllocNil()));
	v.Set(_T("visible"), vVisibleProp);
	v.Set(_T("enabled"), vEnabledProp);
	v.Set(_T("focused"), vFocusedProp);
	v.Set(_T("rect"), vRectProp);
	return v;
}
#endif // DIALECT_ENT

//////////////////////////////////////////////////////////////////////
// Dialect MenuBar Class
//////////////////////////////////////////////////////////////////////

#include "QMenuBar.h"

void FreeMenuBar(void *pVoid)
{
	delete (CQMenuBar*)pVoid;
}

VALUE HandleMenuBar(CQMenuBar *pQMenuBar, int event, VALUE arg)
{
	VALUE self = pQMenuBar->GetSelf();//m_vSelf;
	if (event == QW_DESTROY) {
		self.Set(_T("parent"), AllocNil());
		self.Set(_T("#pQ"), AllocNil());
	}
	else if (event == QW_INITMENU) {
		// arg == menuItemName (ex. "&File")
		return ExecMethod(self, _T("onInitMenu"), arg);
	}
	else if (event == QW_MENUSELECT) {
		// arg == menu item selected
		return ExecMethod(self, _T("onMenuSelect"), arg);
	}
	return VALUE();
}

CORE_FUNC(MenuBar__Init__)
{
	// gui.menubar(parent, rect, menuArray, <optionsFrame>)

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("gui.menubar() takes 3 or 4 arguments");
	}
	REQUIRE_ARRAY(2);
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		CQMenuBar::BorderStyle bs = CQMenuBar::bsNone;	// Default
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			VALUE v = args[3][_T("border")];
			if (v.Type() != stUndefined) {
				CString b = v.String();
				b.MakeLower();
				if (b == _T("none")) bs = CQMenuBar::bsNone;
				else if (b == _T("line")) bs = CQMenuBar::bsLine;
				else if (b == _T("raised")) bs = CQMenuBar::bsRaised;
				else if (b == _T("sunken")) bs = CQMenuBar::bsSunken;
			}
		}
		CQMenuBar* pQMenuBar = new CQMenuBar(pQWindow, rc, args[2], bs, HandleMenuBar, *self);
		self->Set(_T("#pQ"), AllocVoid(pQMenuBar, FreeMenuBar, _T("menubar")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(MenuBar_ReadBorder)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQMenuBar);
	switch (pQ->GetBorderStyle()) {
		case CQMenuBar::bsNone:	return AllocSymbol(_T("none"));
		case CQMenuBar::bsLine:	return AllocSymbol(_T("line"));
		case CQMenuBar::bsRaised:	return AllocSymbol(_T("raised"));
		case CQMenuBar::bsSunken:	return AllocSymbol(_T("sunken"));
	}
	return AllocNil();
}

CORE_FUNC(MenuBar_WriteBorder)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQMenuBar);
	CString style = args[1].String();
	style.MakeLower();
	if (style == _T("none")) pQ->SetBorderStyle(CQMenuBar::bsNone);
	else if (style == _T("line")) pQ->SetBorderStyle(CQMenuBar::bsLine);
	else if (style == _T("raised")) pQ->SetBorderStyle(CQMenuBar::bsRaised);
	else if (style == _T("sunken")) pQ->SetBorderStyle(CQMenuBar::bsSunken);
	return AllocNil();
}

VALUE MenuBarClass()
{
	VALUE MenuBarClass = AllocClass(_T("menubar"),VALUE());
	MenuBarClass.Set(_T("__init__"),AllocFunction(MenuBar__Init__,GUI_MODULE_NAME));
	MenuBarClass.Set(_T("border"), AllocProperty(
		AllocFunction(MenuBar_ReadBorder, GUI_MODULE_NAME),
		AllocFunction(MenuBar_WriteBorder, GUI_MODULE_NAME),
		AllocNil()));
	MenuBarClass.Set(_T("visible"), vVisibleProp);
	MenuBarClass.Set(_T("enabled"), vEnabledProp);
	MenuBarClass.Set(_T("focused"), vFocusedProp);
	MenuBarClass.Set(_T("rect"), vRectProp);
	return MenuBarClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Edit Class
//////////////////////////////////////////////////////////////////////

#include "QEdit.h"

void FreeEdit(void *pVoid)
{
	delete (CQEdit*)pVoid;
}

VALUE HandleEdit(CQEdit *pQEdit, int event, VALUE arg)
{
	VALUE vResult, self = pQEdit->GetSelf();
	switch (event) {
		case QW_DESTROY:
			self.Set(_T("parent"),AllocNil());
			self.Set(_T("#pQ"),AllocNil());
			break;
		case QW_CHANGE:
			vResult = ExecMethod(self, _T("onChange"));
			break;
		case QW_UPDATE:
			vResult = ExecMethod(self, _T("onUpdate"));
			break;
		case QW_FOCUS:
			vResult = ExecMethod(self, _T("onFocus"));
			break;
		case QW_UNFOCUS:
			vResult = ExecMethod(self, _T("onUnfocus"));
			break;
		case QW_CLICK:
			vResult = ExecMethod(self, _T("onClick"));
			break;
		case QW_RCLICK:
			vResult = ExecMethod(self, _T("onRClick"));
			break;
	}
	return vResult;
}

CORE_FUNC(Edit__Init__)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("Invalid number of arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	REQUIRE_STRING(1);
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		VALUE vFont;
		int options = QES_AUTOHSCROLL | QES_BORDER;	// Default options
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			ExtractOption(args[3], _T("autohscroll"), QES_AUTOHSCROLL, options);
			ExtractOption(args[3], _T("readonly"), QES_READONLY, options);
			ExtractOption(args[3], _T("password"), QES_PASSWORD, options);
			vFont = args[3][_T("font")];
		}
		CQEdit* pQEdit = new CQEdit(pQWindow,args[1].String(), rc, false,
			vFont, options, HandleEdit, *self);
		self->Set(_T("#pQ"),AllocVoid(pQEdit,FreeEdit,_T("edit")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(Note__Init__)
{
	// (Currently) Only difference between note and edit type is multiline
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("Invalid number of arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	REQUIRE_STRING(1);
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		VALUE vFont;
		int options = QES_AUTOVSCROLL | QES_BORDER;	// Default options
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			ExtractOption(args[3], _T("autovscroll"), QES_AUTOVSCROLL, options);
			ExtractOption(args[3], _T("autohscroll"), QES_AUTOHSCROLL, options);
			ExtractOption(args[3], _T("vscroll"), QES_VSCROLL, options);
			ExtractOption(args[3], _T("hscroll"), QES_HSCROLL, options);
			ExtractOption(args[3], _T("readonly"), QES_READONLY, options);
			ExtractOption(args[3], _T("border"), QES_BORDER, options);
			vFont = args[3][_T("font")];
		}
		CQEdit* pQEdit = new CQEdit(pQWindow,args[1].String(), rc, true,
			vFont, options, HandleEdit, *self);
		self->Set(_T("#pQ"),AllocVoid(pQEdit,FreeEdit,_T("note")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

/*CORE_FUNC(Edit_SetText) //(self,argCount,args[],pInterp)
{
	BEGIN_METHOD(CQEdit);
	REQUIRE_ARGS(1);
	pQ->SetWindowText(args[0].String());
	return AllocNil();
}

CORE_FUNC(Edit_GetText) //(self,argCount,args[],pInterp)
{
	BEGIN_METHOD(CQEdit);
	REQUIRE_ARGS(0);
	CString text;
	pQ->GetWindowText(text);
	return text;
}*/

CORE_FUNC(Edit_ReplaceSel)
{
	BEGIN_METHOD(CQEdit);
	REQUIRE_ARGS(1);
	pQ->ReplaceSel(args[0].String());
	return args[0];
}

CORE_FUNC(Edit_ReadSelection)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQEdit);
	int start, end;
	pQ->GetSel(start, end);
	VALUE vArray = AllocArray(2);
	vArray.Set(1, start + 1);
	vArray.Set(2, end + 1);
	return vArray;
}

CORE_FUNC(Edit_WriteSelection)
{
	REQUIRE_ARGS(2);
	REQUIRE_ARRAY(1);
	BEGIN_METHOD(CQEdit);
	pQ->SetSel(args[1][1].Integer() - 1, args[1][2].Integer() - 1);
	return args[1];
}

CORE_FUNC(Edit_ReadTopline)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQEdit);
	return pQ->GetFirstVisibleLine() + 1;
}

CORE_FUNC(Edit_WriteTopline)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQEdit);
	pQ->LineScroll(args[1].Integer() - 1 - pQ->GetFirstVisibleLine());
	return pQ->GetFirstVisibleLine();
}

CORE_FUNC(Edit_ReadCurrentLine)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQEdit);
	return pQ->LineFromChar() + 1;
}

CORE_FUNC(Edit_WriteCurrentLine)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQEdit);
	int lineIndex = pQ->LineIndex(args[1].Integer() - 1);
	pQ->SetSel(lineIndex, lineIndex);
	return pQ->LineFromChar() + 1;
}

VALUE EditClass()
{
	VALUE editClass = AllocClass(_T("edit"),VALUE());
	editClass.Set(_T("__init__"),AllocFunction(Edit__Init__,GUI_MODULE_NAME));
	editClass.Set(_T("replaceSel"), AllocFunction(Edit_ReplaceSel,GUI_MODULE_NAME));
	//editClass.Set(_T("settext"),AllocFunction(Edit_SetText,GUI_MODULE_NAME));
	//editClass.Set(_T("gettext"),AllocFunction(Edit_GetText,GUI_MODULE_NAME));
	//editClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	editClass.Set(_T("selection"), AllocProperty(
		AllocFunction(Edit_ReadSelection, GUI_MODULE_NAME),
		AllocFunction(Edit_WriteSelection, GUI_MODULE_NAME),
		AllocNil()));
	editClass.Set(_T("text"), vTextProp);
	editClass.Set(_T("visible"), vVisibleProp);
	editClass.Set(_T("enabled"), vEnabledProp);
	editClass.Set(_T("focused"), vFocusedProp);
	editClass.Set(_T("rect"), vRectProp);
	return editClass;
}

VALUE NoteClass()
{
	// The __init__ function is different, but the rest are the same as edit class
	VALUE noteClass = AllocClass(_T("note"),VALUE());
	noteClass.Set(_T("__init__"),AllocFunction(Note__Init__,GUI_MODULE_NAME));
	noteClass.Set(_T("replaceSel"), AllocFunction(Edit_ReplaceSel,GUI_MODULE_NAME));
	//noteClass.Set(_T("settext"),AllocFunction(Edit_SetText,GUI_MODULE_NAME));
	//noteClass.Set(_T("gettext"),AllocFunction(Edit_GetText,GUI_MODULE_NAME));
	//noteClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	noteClass.Set(_T("selection"), AllocProperty(
		AllocFunction(Edit_ReadSelection, GUI_MODULE_NAME),
		AllocFunction(Edit_WriteSelection, GUI_MODULE_NAME),
		AllocNil()));
	noteClass.Set(_T("currentLine"), AllocProperty(
		AllocFunction(Edit_ReadCurrentLine, GUI_MODULE_NAME),
		AllocFunction(Edit_WriteCurrentLine, GUI_MODULE_NAME),
		AllocNil()));
	noteClass.Set(_T("topline"), AllocProperty(
		AllocFunction(Edit_ReadTopline, GUI_MODULE_NAME),
		AllocFunction(Edit_WriteTopline, GUI_MODULE_NAME),
		AllocNil()));
	noteClass.Set(_T("text"), vTextProp);
	noteClass.Set(_T("visible"), vVisibleProp);
	noteClass.Set(_T("enabled"), vEnabledProp);
	noteClass.Set(_T("focused"), vFocusedProp);
	noteClass.Set(_T("rect"), vRectProp);
	return noteClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Grid Class
//////////////////////////////////////////////////////////////////////

#include "QGrid.h"

void FreeGrid(void *pVoid)
{
	delete (CQGrid*)pVoid;
}

VALUE HandleGrid(CQGrid *pQGrid, int event, VALUE arg)
{
	VALUE vResult, self = pQGrid->GetSelf();
	if (event == QW_DESTROY) {
		self.Set(_T("parent"),AllocNil());
		self.Set(_T("#pQ"),AllocNil());
	}
	else if (event == QW_CLICK)	vResult = ExecMethod(self, _T("onClick"));
	else if (event == QW_SELCHANGE)	vResult = ExecMethod(self, _T("onSelChange"));
	else if (event == QW_DOUBLECLICK) vResult = ExecMethod(self, _T("onDoubleClick"));

	return vResult;
}

CORE_FUNC(Grid__Init__)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 4) && (argCount != 5)) {
		ERR("Expecting 4 or 5 arguments to grid constructor");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();

	CRect rc;
	if (!ValueToRect(args[1], pInterp, rc)) return AllocNil();	// Size

	REQUIRE_INTEGER(2);	int rows = args[2].Integer();
	REQUIRE_ARRAY(3); VALUE vColWidths = args[3];
	int cols = vColWidths.Length();

	int* widths = new int[cols];
	for (int idx = 0; idx < cols; ++idx) {
		if (vColWidths[idx + 1].Type() == stInteger) {
			widths[idx] = vColWidths[idx + 1].Integer();
		}
		else {
			delete[] widths;
			pInterp->RuntimeError(_T("All column widths must be integers"));
			return AllocNil();
		}
	}

	int rowHeight = 16;
	int colHeadersHeight = 16;

	VALUE vEditFont, vLabelFont;
	bool bAutoScroll = true;
	if ((argCount == 5) && (args[4].Type() == stFrame)) {
		VALUE v = args[4][_T("colheaders")];
		if ((v.Type() != stUndefined) && !v.Boolean()){
			colHeadersHeight = 0;
		}
		v = args[4][_T("autoscroll")];
		if (v.Type() != stUndefined) {
			bAutoScroll = v.Boolean();
		}
		vEditFont = args[4][_T("cellfont")];
		vLabelFont = args[4][_T("headerfont")];
	}

	CQGrid* pQGrid = new CQGrid(pQWindow, rc, rows, cols - 1, rowHeight, (widths + 1),
		colHeadersHeight, widths[0], vEditFont, vLabelFont, HandleGrid, *self);
	self->Set(_T("#pQ"),AllocVoid(pQGrid, FreeGrid, _T("grid")));
	pQGrid->SetAutoScroll(bAutoScroll ? 1 : 0);

	// Some read only props...
	self->Set(_T("parent"), AllocProperty(
		AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
		AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
		(int)args[0].ClassInstance()));

	delete[] widths;
	return VALUE();
}

CORE_FUNC(Grid__Del__) //(self,argCount,args[],pInterp)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	// most of the destruction is automatic
	return VALUE();
}

CORE_FUNC(Grid_SetText) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(3);
	REQUIRE_INTEGER(0);
	REQUIRE_INTEGER(1);

	BEGIN_METHOD(CQGrid);
	pQ->SetText(args[0].Integer(), args[1].Integer(), args[2].String());
	return AllocNil();
}

CORE_FUNC(Grid_GetText) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(2);
	REQUIRE_INTEGER(0);
	REQUIRE_INTEGER(1);

	BEGIN_METHOD(CQGrid);
	return pQ->GetText(args[0].Integer(), args[1].Integer());
}

CORE_FUNC(Grid_ReadSelection) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(1);	// Read prop funcs always have one
	BEGIN_METHOD(CQGrid);
	int row, col, rowsel, colsel;
	pQ->GetSelection(&row, &col, &rowsel, &colsel);
	VALUE vArray = AllocArray(4);
	vArray.Set(1, row + 1);
	vArray.Set(2, col + 1);
	vArray.Set(3, rowsel + 1);
	vArray.Set(4, colsel + 1);
	return vArray;
}

CORE_FUNC(Grid_WriteSelection) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(2);	// Write prop funcs always have two
	BEGIN_METHOD(CQGrid);

	VALUE vArray = args[1];
	if ((vArray.Type() != stArray) || (vArray[1].Type() != stInteger) ||
		(vArray[2].Type() != stInteger) || (vArray[3].Type() != stInteger) ||
		(vArray[4].Type() != stInteger))
	{
		ERR("grid.selection property requires an array of four integers");
	}
	else {
		int row = vArray[1].Integer() - 1;
		int col = vArray[2].Integer() - 1;
		int rowsel = vArray[3].Integer() - 1;
		int colsel = vArray[4].Integer() - 1;
		pQ->SetSelection(row, col, rowsel, colsel);
	}
	return VALUE();	// Not used in this case (read func generates value)
}

CORE_FUNC(Grid_EditCell)
{
	REQUIRE_ARGS(2);	// Write prop funcs always have two
	REQUIRE_INTEGER(0);
	REQUIRE_INTEGER(1);
	BEGIN_METHOD(CQGrid);
	int row = args[0].Integer() - 1;
	int col = args[1].Integer() - 1;
	if ((row < pQ->GetRowCount()) && (col < pQ->GetColumnCount())) {
		pQ->SetSelection(row, col);
		pQ->Edit(TRUE);
	}
	else ERR("Invalid row or column index");

	return AllocNil();
}

CORE_FUNC(Grid_Clear)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQGrid);
	pQ->Clear();
	return AllocNil();
}

CORE_FUNC(Grid_ReadRowCount)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQGrid);
	return pQ->GetRowCount();
}

CORE_FUNC(Grid_WriteRowCount)
{
	REQUIRE_ARGS(2);
	REQUIRE_INTEGER(1);
	BEGIN_METHOD(CQGrid);
	pQ->SetRowCount(args[1].Integer());
	return args[1];
}

CORE_FUNC(Grid_ReadColCount)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQGrid);
	return pQ->GetColumnCount();
}

CORE_FUNC(Grid_ReadContents)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQGrid);
	return pQ->GetContents();
}

CORE_FUNC(Grid_WriteContents)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQGrid);
	if (!pQ->SetContents(args[1])) {
		ERR("Failed to set contents of grid.  Possibly an incorrect size array.");
	}
	return true;
}

CORE_FUNC(Grid_ReadRowHeaders)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQGrid);
	return pQ->GetRowHeaders();
}

CORE_FUNC(Grid_WriteRowHeaders)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQGrid);
	if (!pQ->SetRowHeaders(args[1])) {
		ERR("Failed to set row headers of grid.  Possibly an incorrect size array.");
	}
	return true;
}

CORE_FUNC(Grid_ReadColHeaders)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQGrid);
	return pQ->GetColHeaders();
}

CORE_FUNC(Grid_WriteColHeaders)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQGrid);
	if (!pQ->SetColHeaders(args[1])) {
		ERR("Failed to set col headers of grid.  Possibly an incorrect size array.");
	}
	return true;
}

VALUE GridClass()
{
	VALUE gridClass = AllocClass(_T("grid"),VALUE());
	gridClass.Set(_T("__init__"),AllocFunction(Grid__Init__,GUI_MODULE_NAME));
	gridClass.Set(_T("__del__"), AllocFunction(Grid__Del__,GUI_MODULE_NAME));
	gridClass.Set(_T("clear"),AllocFunction(Grid_Clear,GUI_MODULE_NAME));
	gridClass.Set(_T("editcell"),AllocFunction(Grid_EditCell,GUI_MODULE_NAME));
	gridClass.Set(_T("getcell"),AllocFunction(Grid_GetText,GUI_MODULE_NAME));
	gridClass.Set(_T("setcell"),AllocFunction(Grid_SetText,GUI_MODULE_NAME));
	gridClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	gridClass.Set(_T("rows"), AllocProperty(
		AllocFunction(Grid_ReadRowCount, GUI_MODULE_NAME),
		AllocFunction(Grid_WriteRowCount, GUI_MODULE_NAME),
		AllocNil()));
	gridClass.Set(_T("cols"), AllocProperty(
		AllocFunction(Grid_ReadColCount, GUI_MODULE_NAME),
		AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
		AllocNil()));
	gridClass.Set(_T("contents"), AllocProperty(
		AllocFunction(Grid_ReadContents, GUI_MODULE_NAME),
		AllocFunction(Grid_WriteContents, GUI_MODULE_NAME),
		AllocNil()));
	gridClass.Set(_T("rowheaders"), AllocProperty(
		AllocFunction(Grid_ReadRowHeaders, GUI_MODULE_NAME),
		AllocFunction(Grid_WriteRowHeaders, GUI_MODULE_NAME),
		AllocNil()));
	gridClass.Set(_T("colheaders"), AllocProperty(
		AllocFunction(Grid_ReadColHeaders, GUI_MODULE_NAME),
		AllocFunction(Grid_WriteColHeaders, GUI_MODULE_NAME),
		AllocNil()));
	gridClass.Set(_T("selection"), AllocProperty(
		AllocFunction(Grid_ReadSelection, GUI_MODULE_NAME),
		AllocFunction(Grid_WriteSelection, GUI_MODULE_NAME),
		AllocNil()));
	gridClass.Set(_T("visible"), vVisibleProp);
	gridClass.Set(_T("enabled"), vEnabledProp);
	gridClass.Set(_T("focused"), vFocusedProp);
	gridClass.Set(_T("rect"), vRectProp);
	return gridClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Label Class
//////////////////////////////////////////////////////////////////////

#include "QScrollBar.h"

void FreeScrollBar(void *pVoid)
{
	delete (CQScrollBar*)pVoid;
}

VALUE HandleScrollBar(CQScrollBar *pQ, int event, VALUE arg)
{
	VALUE vResult, self = pQ->GetSelf();
	if (event == QW_DESTROY) {
		self.Set(_T("parent"),AllocNil());
		self.Set(_T("#pQ"),AllocNil());
	}
	else if (event == QW_VSCROLL)	{
		vResult = ExecMethod(self, _T("onScroll"));
	}
	else if (event == QW_HSCROLL)	{
		vResult = ExecMethod(self, _T("onScroll"));
	}
	return vResult;
}

CORE_FUNC(ScrollBar__Init__)
{
	// gui.scrollbar(parent, bHorz, rect, min, max, pageSize)

	ASSERT_SELF(GUI_MODULE_NAME);
	REQUIRE_ARGS(6);
	REQUIRE_INTEGER(3);
	REQUIRE_INTEGER(4);
	REQUIRE_INTEGER(5);
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		CQScrollBar* pQ = new CQScrollBar(pQWindow, args[1].Boolean(), rc,
			args[3].Integer(), args[4].Integer(), args[5].Integer(), 1,
			args[5].Integer(),// - 1,
			HandleScrollBar, *self);
		self->Set(_T("#pQ"),AllocVoid(pQ, FreeScrollBar, _T("scrollbar")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(ScrollBar_ReadPosition)
{
	BEGIN_METHOD(CQScrollBar);
	REQUIRE_ARGS(1);	// Read prop
	return pQ->GetScrollPos();
}

CORE_FUNC(ScrollBar_WritePosition)
{
	REQUIRE_ARGS(2);	// Write prop
	REQUIRE_INTEGER(1);
	BEGIN_METHOD(CQScrollBar);
	pQ->SetScrollPos(args[1].Integer());
	return AllocNil();
}

CORE_FUNC(ScrollBar_ReadRange)
{
	BEGIN_METHOD(CQScrollBar);
	REQUIRE_ARGS(1);	// Read prop
	int rMin, rMax;
	pQ->GetScrollRange(&rMin, &rMax);
	VALUE vArray = AllocArray(2);
	vArray.Set(1, rMin);
	vArray.Set(2, rMax);
	return vArray;
}

CORE_FUNC(ScrollBar_WriteRange)
{
	REQUIRE_ARGS(2);	// Write prop
	REQUIRE_ARRAY(1);
	BEGIN_METHOD(CQScrollBar);
	VALUE vArray = args[1];
	if ((vArray[1].Type() != stInteger) || (vArray[2].Type() != stInteger)) {
		ERR("Both elements of range array must be integer");
	}
	pQ->SetScrollRange(vArray[1].Integer(), vArray[2].Integer());
	return AllocNil();
}

VALUE ScrollBarClass()
{
	VALUE sbClass = AllocClass(_T("scrollbar"),VALUE());
	sbClass.Set(_T("__init__"),AllocFunction(ScrollBar__Init__,GUI_MODULE_NAME));
	sbClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	sbClass.Set(_T("position"), AllocProperty(
		AllocFunction(ScrollBar_ReadPosition, GUI_MODULE_NAME),
		AllocFunction(ScrollBar_WritePosition, GUI_MODULE_NAME),
		AllocNil()));
	sbClass.Set(_T("range"), AllocProperty(
		AllocFunction(ScrollBar_ReadRange, GUI_MODULE_NAME),
		AllocFunction(ScrollBar_WriteRange, GUI_MODULE_NAME),
		AllocNil()));
	sbClass.Set(_T("visible"), vVisibleProp);
	sbClass.Set(_T("enabled"), vEnabledProp);
	sbClass.Set(_T("focused"), vFocusedProp);
	sbClass.Set(_T("rect"), vRectProp);
	return sbClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Label Class
//////////////////////////////////////////////////////////////////////

#include "QLabel.h"

void FreeLabel(void *pVoid)
{
	delete (CQLabel*)pVoid;
}

VALUE HandleLabel(CQLabel *pQLabel, int event, VALUE arg)
{
	VALUE vResult, self = pQLabel->GetSelf();
	if (event == QW_DESTROY) {
		self.Set(_T("parent"),AllocNil());
		self.Set(_T("#pQ"),AllocNil());
	}
	else if (event == QW_CLICK)	{
		vResult = ExecMethod(self, _T("onClick"));
	}
	return vResult;
}

CORE_FUNC(Label__Init__)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 3) && (argCount != 4)) {
		ERR("gui.label() requires 3 or 4 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	REQUIRE_STRING(1);
	CRect rc;
	if (ValueToRect(args[2], pInterp, rc)) {
		int options = 0;
		VALUE vFont;
		CQBitmap bitmap, *pBitmap = NULL;
		if ((argCount == 4) && (args[3].Type() == stFrame)) {
			ExtractOption(args[3], _T("center"), QLAS_CENTER, options);
			VALUE v = args[3][_T("bitmap")];
			if ((v.Type() == stClassInstance) && v.Isa(_T("bitmap"))) {
				pBitmap = (CQBitmap*)v.Get(_T("#pQ")).Void();
			}
			else if ((v.Type() == stString) && bitmap.LoadFile(v.String())) {
				pBitmap = &bitmap;
			}
			else if ((v.Type() == stInteger) && bitmap.LoadBitmap(v.Integer())) {
				pBitmap = &bitmap;
			}
			vFont = args[3][_T("font")];
		}
		CQLabel* pQ = new CQLabel(pQWindow,args[1].String(), rc, options, pBitmap, vFont,
			HandleLabel, *self);
		self->Set(_T("#pQ"), AllocVoid(pQ, FreeLabel, _T("label")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(Label_WriteBitmap)
{
	BEGIN_METHOD(CQLabel);
	REQUIRE_ARGS(2);
	CQBitmap bitmap, *pBitmap = NULL;
	VALUE vb = args[1];
	if ((vb.Type() == stClassInstance) && vb.Isa(_T("bitmap"))) {
		pBitmap = (CQBitmap*)vb.Get(_T("#pQ")).Void();
	}
	else if ((vb.Type() == stString) && bitmap.LoadFile(vb.String())) {
		pBitmap = &bitmap;
	}
	else if ((vb.Type() == stInteger) && bitmap.LoadBitmap(vb.Integer())) {
		pBitmap = &bitmap;
	}
	pQ->SetNewBitmap(pBitmap);
	return AllocNil();
}

CORE_FUNC(Label_WriteText)
{
	// Can't use the generic one, since the label may be showing
	// a bitmap, so we may have to change window styles
	ASSERT(argCount == 2);	// Write prop always has one
	BEGIN_METHOD(CQLabel);
	pQ->SetText(args[1].String());
	return AllocNil();
}

CORE_FUNC(Label_TextWidth)
{
	BEGIN_METHOD(CQLabel);
	REQUIRE_ARGS(1);
	return pQ->TextWidth(args[0].String());
}

CORE_FUNC(Label_TextHeight)
{
	BEGIN_METHOD(CQLabel);
	REQUIRE_ARGS(1);
	return pQ->TextHeight(args[0].String());
}

VALUE LabelClass()
{
	VALUE labelClass = AllocClass(_T("label"),VALUE());
	labelClass.Set(_T("__init__"),AllocFunction(Label__Init__,GUI_MODULE_NAME));
	labelClass.Set(_T("textWidth"),AllocFunction(Label_TextWidth,GUI_MODULE_NAME));
	labelClass.Set(_T("textHeight"),AllocFunction(Label_TextHeight,GUI_MODULE_NAME));
	labelClass.Set(_T("bitmap"), AllocProperty(
		AllocNil(),
		AllocFunction(Label_WriteBitmap, GUI_MODULE_NAME),
		AllocNil()));
	labelClass.Set(_T("text"), AllocProperty(
		AllocFunction(Generic_ReadText, GUI_MODULE_NAME),
		AllocFunction(Label_WriteText, GUI_MODULE_NAME),
		AllocNil()));
	labelClass.Set(_T("visible"), vVisibleProp);
	labelClass.Set(_T("enabled"), vEnabledProp);
	labelClass.Set(_T("rect"), vRectProp);
	return labelClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect Layout Class
//////////////////////////////////////////////////////////////////////

#if defined(DIALECT_ENT)

#include "QLayout.h"

void FreeLayout(void *pVoid)
{
	delete (CQLayout*)pVoid;
}

VALUE HandleLayout(CQLayout *pQLayout, int event, VALUE arg)
{
	VALUE vResult, self = pQLayout->GetSelf();
	if (event == QW_DESTROY) {
		self.Set(_T("parent"),AllocNil());
		self.Set(_T("#pQ"),AllocNil());
	}
	else if (event == QW_ITEMINFO)	{
		vResult = ExecMethod(self, _T("onItemInfo"), arg);
	}
	else if (event == QW_MENUSELECT)	{
		vResult = ExecMethod(self, _T("onMenuSelect"), arg);
	}
	return vResult;
}

CORE_FUNC(Layout__Init__)
{
	// gui.layout(parent, rect, layoutFrame, dataFrame, <optionsFrame>)
	//OutputDebugString(_T("**** 1\n"));

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 4) && (argCount != 5)) {
		ERR("Expecting 4 or 5 arguments to layout constructor");
	}

	//OutputDebugString(_T("**** 2\n"));

	if (args[2].Type() == stNil) {
		args[2] = AllocFrame();
	}
	else if (args[2].Type() != stFrame) {
		ERR("Third arg to gui.layout must be layout frame");
	}

	//OutputDebugString(_T("**** 3\n"));

	if (args[3].Type() == stNil) {
		args[3] = AllocFrame();
	}
	else if (args[3].Type() != stFrame) {
		ERR("Fourth arg to gui.layout must be data frame");
	}

	//OutputDebugString(_T("**** 4\n"));

	CQLayout* pQ = NULL;
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	//OutputDebugString(_T("**** 5\n"));

	if (ValueToRect(args[1], pInterp, rc)) {
		VALUE vRuleContext;
		int options = QLYOS_BUTTONS;
	//OutputDebugString(_T("**** 6\n"));

		if ((argCount == 5) && (args[4].Type() == stFrame)) {
	//OutputDebugString(_T("**** 7\n"));

			ExtractOption(args[4], _T("navigation"), QLYOS_BUTTONS, options);
			ExtractOption(args[4], _T("singlesection"), QLYOS_SINGLESECT, options);
			ExtractOption(args[4], _T("noformname"), QLYOS_NOFORMNAME, options);
			VALUE v = args[4][_T("rulecontext")];
			if (v.Type() == stFrame) vRuleContext = v;
	//OutputDebugString(_T("**** 8\n"));

		}
		_TRY {
	//OutputDebugString(_T("**** 9\n"));

			pQ = new CQLayout(pQWindow, rc, args[2], args[3], vRuleContext,
				options, HandleLayout, *self);
	//OutputDebugString(_T("**** 10\n"));

			self->Set(_T("#pQ"),AllocVoid(pQ, FreeLayout, _T("Layout")));
			self->Set(_T("parent"), AllocProperty(
				AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
				AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
				(int)args[0].ClassInstance()));
	//OutputDebugString(_T("**** 11\n"));

		}
		_CATCH_ALL {
			if (pQ != NULL) {
				delete pQ;
				pQ = NULL;
			}
		}

		if (pQ == NULL) {
			ERR("GUI module was unable to create low level layout window.");
		}
	}

	//OutputDebugString(_T("**** 12\n"));

	return AllocNil();
}

CORE_FUNC(Layout_Update)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(0);
	pQ->Update();
	return true;
}

CORE_FUNC(Layout_ReadSection)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(1);
	if (pQ && pQ->m_pLayoutChild) {
		return pQ->m_pLayoutChild->m_iTopSection + 1;
	}
	return 0;
}

CORE_FUNC(Layout_WriteSection)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(2);
	int section = args[1].Integer() - 1;
	if (pQ && pQ->m_pLayoutChild && (section >= 0) && (section < pQ->m_pLayoutChild->m_NSections)) {
		pQ->m_pLayoutChild->UpdateVisibility();
		pQ->m_pLayoutChild->Reposition(section);
	}
	return args[1];
}

CORE_FUNC(Layout_ReadPage)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(1);
	if (pQ && pQ->m_pLayoutChild) {
		return pQ->m_pLayoutChild->GetPage();
	}
	return 0;
}

CORE_FUNC(Layout_WritePage)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(2);
	int page = args[1].Integer();
	if (pQ && pQ->m_pLayoutChild) {
		pQ->m_pLayoutChild->GotoPage(page);
	}
	return args[1];
}

CORE_FUNC(Layout_ReadBinDir)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(1);
	if (pQ && pQ->m_pLayoutChild) {
		return pQ->m_pLayoutChild->m_BinDir;
	}
	return 0;
}

CORE_FUNC(Layout_WriteBinDir)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(2);
	if (pQ && pQ->m_pLayoutChild) {
		pQ->m_pLayoutChild->m_BinDir = args[1].String();
	}
	return args[1];
}

CORE_FUNC(Layout_sectionCount)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(0);
	if (pQ && pQ->m_pLayoutChild) {
		return pQ->m_pLayoutChild->m_NSections;
	}
	return 0;
}

CORE_FUNC(Layout_pageCount)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(0);
	if (pQ && pQ->m_pLayoutChild) {
		return pQ->m_pLayoutChild->PageCount();
	}
	return 0;
}

CORE_FUNC(Layout_ReadData)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(1);
	return pQ->GetData();
}

CORE_FUNC(Layout_WriteData)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(2);
	if (args[1].Type() == stNil) {
		args[1] = AllocFrame();
	}
	else if (args[1].Type() != stFrame) {
		ERR("layout.data must be a frame");
	}
	pQ->SetData(args[1]);
	return args[1];
}

CORE_FUNC(Layout_ReadContext)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(1);
	return pQ->GetRuleContext();
}

CORE_FUNC(Layout_WriteContext)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(2);
	if (args[1].Type() == stNil) {
		args[1] = AllocFrame();
	}
	else if (args[1].Type() != stFrame) {
		ERR("layout.context must be a frame");
	}
	pQ->SetRuleContext(args[1]);
	return args[1];
}

CORE_FUNC(Layout_ReadSingleSection)
{
	REQUIRE_ARGS(1);
	BEGIN_METHOD(CQLayout);
	return pQ->GetSingleSection();
}

CORE_FUNC(Layout_WriteSingleSection)
{
	REQUIRE_ARGS(2);
	BEGIN_METHOD(CQLayout);
	return pQ->SetSingleSection(args[1].Boolean());
}

CORE_FUNC(Layout_ReadFlashMenu)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(1);
	return pQ->GetFlashMenu();
}

CORE_FUNC(Layout_WriteFlashMenu)
{
	BEGIN_METHOD(CQLayout);
	REQUIRE_ARGS(2);
	if ((args[1].Type() != stArray) && (args[1].Type() != stNil)) {
		ERR("layout.setflashmenu() requires an array or nil argument");
	}
	pQ->SetFlashMenu(args[1]);
	return args[1];
}

VALUE LayoutClass()
{
	VALUE LayoutClass = AllocClass(_T("Layout"),VALUE());
	LayoutClass.Set(_T("__init__"),AllocFunction(Layout__Init__, GUI_MODULE_NAME));
	LayoutClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	LayoutClass.Set(_T("update"),AllocFunction(Layout_Update, GUI_MODULE_NAME));
	//LayoutClass.Set(_T("singlesection"), AllocProperty(
	//	AllocFunction(Layout_ReadSingleSection, GUI_MODULE_NAME),
	//	AllocFunction(Layout_WriteSingleSection, GUI_MODULE_NAME),
	//	AllocNil()));
	LayoutClass.Set(_T("rulecontext"), AllocProperty(
		AllocFunction(Layout_ReadContext, GUI_MODULE_NAME),
		AllocFunction(Layout_WriteContext, GUI_MODULE_NAME),
		AllocNil()));
	LayoutClass.Set(_T("flashmenu"), AllocProperty(
		AllocFunction(Layout_ReadFlashMenu, GUI_MODULE_NAME),
		AllocFunction(Layout_WriteFlashMenu, GUI_MODULE_NAME),
		AllocNil()));
	LayoutClass.Set(_T("data"), AllocProperty(
		AllocFunction(Layout_ReadData, GUI_MODULE_NAME),
		AllocFunction(Layout_WriteData, GUI_MODULE_NAME),
		AllocNil()));
	LayoutClass.Set(_T("section"), AllocProperty(
		AllocFunction(Layout_ReadSection, GUI_MODULE_NAME),
		AllocFunction(Layout_WriteSection, GUI_MODULE_NAME),
		AllocNil()));
	LayoutClass.Set(_T("page"), AllocProperty(
		AllocFunction(Layout_ReadPage, GUI_MODULE_NAME),
		AllocFunction(Layout_WritePage, GUI_MODULE_NAME),
		AllocNil()));
	LayoutClass.Set(_T("bindir"), AllocProperty(
		AllocFunction(Layout_ReadBinDir, GUI_MODULE_NAME),
		AllocFunction(Layout_WriteBinDir, GUI_MODULE_NAME),
		AllocNil()));
	LayoutClass.Set(_T("visible"), vVisibleProp);
	LayoutClass.Set(_T("enabled"), vEnabledProp);
	LayoutClass.Set(_T("focused"), vFocusedProp);
	LayoutClass.Set(_T("rect"), vRectProp);
	//LayoutClass.Set(_T("gotosection"), AllocFunction(Layout_gotoSection, GUI_MODULE_NAME));
	//LayoutClass.Set(_T("gotopage"), AllocFunction(Layout_gotoPage, GUI_MODULE_NAME));
	LayoutClass.Set(_T("sectioncount"), AllocFunction(Layout_sectionCount, GUI_MODULE_NAME));
	LayoutClass.Set(_T("pagecount"), AllocFunction(Layout_pageCount, GUI_MODULE_NAME));
	return LayoutClass;
}

#endif // #if defined(DIALECT_ENT)

//////////////////////////////////////////////////////////////////////
// Dialect ListBox Class
//////////////////////////////////////////////////////////////////////

#include "QListBox.h"

void FreeListBox(void *pVoid)
{
	delete (CQListBox*)pVoid;
}

VALUE HandleListBox(CQListBox *pQ, int event, VALUE arg)
{
	VALUE vResult, self = pQ->GetSelf();
	switch (event) {
		case QW_DESTROY:
			self.Set(_T("parent"),AllocNil());
			self.Set(_T("#pQ"),AllocNil());
			break;
		case QW_RCLICK:
			vResult = ExecMethod(self, _T("onRClick"));
			break;
		case QW_DOUBLECLICK:
			vResult = ExecMethod(self, _T("onDoubleClick"));
			break;
		case QW_SELCHANGE:
			vResult = ExecMethod(self, _T("onSelChange"));
			break;
		case QW_FOCUS:
			vResult = ExecMethod(self, _T("onFocus"));
			break;
		case QW_UNFOCUS:
			vResult = ExecMethod(self, _T("onUnfocus"));
			break;
	}
	return vResult;
}

CORE_FUNC(ListBox__Init__)
{
	// gui.listbox(parent, rect, <optionsFrame>)

	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 2) && (argCount != 3)) {
		ERR("gui.listbox contructor requires 2 or 3 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		int options = QLBS_BORDER;	// Default options
		if ((argCount == 3) && (args[2].Type() == stFrame)) {
			ExtractOption(args[2], _T("border"), QLBS_BORDER, options);
			ExtractOption(args[2], _T("sort"), QLBS_SORT, options);
			ExtractOption(args[2], _T("multi"), QLBS_MULTISELECT, options);
		}
		CQListBox* pQ = new CQListBox(pQWindow, rc, options, HandleListBox, *self);
		self->Set(_T("#pQ"),AllocVoid(pQ, FreeListBox, _T("listbox")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(ListBox__Del__) //(self,argCount,args[],pInterp)
{
	ASSERT_SELF(GUI_MODULE_NAME);
	// most of the destruction is automatic
	return VALUE();
}

CORE_FUNC(ListBox_AddString) //(self,argCount,args[],pInterp)
{
	BEGIN_METHOD(CQListBox);
	REQUIRE_ARGS(1);
	return pQ->AddString(args[0].String()) + 1;
}

CORE_FUNC(ListBox_DelString) //(self,argCount,args[],pInterp)
{
	BEGIN_METHOD(CQListBox);
	REQUIRE_ARGS(1);
	REQUIRE_INTEGER(0);
	return pQ->DeleteString(args[0].Integer() - 1);
}

CORE_FUNC(ListBox_Count) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQListBox);
	return pQ->GetCount();
}

CORE_FUNC(ListBox_ReadSelect) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CQListBox);
	return pQ->GetCurSel() + 1;
}

CORE_FUNC(ListBox_WriteSelect) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(2);	// Write prop always has one
	BEGIN_METHOD(CQListBox);

	if (args[1].Type() == stNil) {
		pQ->SetCurSel(-1);
	}
	else if (args[1].Type() == stInteger) {
		pQ->SetCurSel(args[1].Integer() - 1);
	}
	else ERR("Invalid arg type assigned to listbox.select property");

	return AllocNil();
}

CORE_FUNC(ListBox_ReadContents)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CQListBox);

	int length = pQ->GetCount();
	VALUE vArray = AllocArray(length);
	for (int idx = 0; idx < length; ++idx) {
		CString text;
		pQ->GetText(idx, text);
		vArray.Set(idx + 1, text);
	}
	return vArray;
}

CORE_FUNC(ListBox_WriteContents)
{
	REQUIRE_ARGS(2);	// Write prop always has one
	BEGIN_METHOD(CQListBox);

	VALUE vData = args[1];

	if (vData.Type() == stNil) {
		pQ->ResetContent();
	}
	else if (vData.Type() == stString) {
		pQ->ResetContent();
		pQ->AddString(vData.String());
	}
	else if (vData.Type() == stArray) {
		pQ->ResetContent();
		int length = vData.Length();
		for (int idx = 0; idx < length; ++idx) {
			pQ->InsertString(idx, vData[idx + 1].String());
		}
	}
	else ERR("Invalid argument type to listbox.content write property");

	return AllocNil();
}

CORE_FUNC(ListBox_ReadTopIndex) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CQListBox);
	int index = pQ->GetTopIndex();
	if (index == LB_ERR) index = -1;
	return index + 1;
}

CORE_FUNC(ListBox_WriteTopIndex) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(2);	// Write prop always has one
	BEGIN_METHOD(CQListBox);

	if (args[1].Type() == stInteger) {
		pQ->SetTopIndex(args[1].Integer() - 1);
	}
	else ERR("Invalid arg type assigned to listbox.topindex property");

	return AllocNil();
}

VALUE ListBoxClass()
{
	VALUE listboxClass = AllocClass(_T("listbox"), VALUE());
	listboxClass.Set(_T("__init__"),AllocFunction(ListBox__Init__, GUI_MODULE_NAME));
	listboxClass.Set(_T("__del__"), AllocFunction(ListBox__Del__, GUI_MODULE_NAME));
	listboxClass.Set(_T("add"),AllocFunction(ListBox_AddString, GUI_MODULE_NAME));
	listboxClass.Set(_T("delete"),AllocFunction(ListBox_DelString, GUI_MODULE_NAME));
	listboxClass.Set(_T("count"),AllocFunction(ListBox_Count, GUI_MODULE_NAME));
	listboxClass.Set(_T("destroy"),AllocFunction(Generic_Destroy, GUI_MODULE_NAME));
	listboxClass.Set(_T("contents"), AllocProperty(
		AllocFunction(ListBox_ReadContents, GUI_MODULE_NAME),
		AllocFunction(ListBox_WriteContents, GUI_MODULE_NAME),
		AllocNil()));
	listboxClass.Set(_T("selection"), AllocProperty(
		AllocFunction(ListBox_ReadSelect, GUI_MODULE_NAME),
		AllocFunction(ListBox_WriteSelect, GUI_MODULE_NAME),
		AllocNil()));
	listboxClass.Set(_T("topindex"), AllocProperty(
		AllocFunction(ListBox_ReadTopIndex, GUI_MODULE_NAME),
		AllocFunction(ListBox_WriteTopIndex, GUI_MODULE_NAME),
		AllocNil()));
	listboxClass.Set(_T("select"), AllocProperty(	// Delete me
		AllocFunction(ListBox_ReadSelect, GUI_MODULE_NAME),
		AllocFunction(ListBox_WriteSelect, GUI_MODULE_NAME),
		AllocNil()));
	listboxClass.Set(_T("visible"), vVisibleProp);
	listboxClass.Set(_T("enabled"), vEnabledProp);
	listboxClass.Set(_T("focused"), vFocusedProp);
	listboxClass.Set(_T("rect"), vRectProp);
	return listboxClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect ComboBox Class
//////////////////////////////////////////////////////////////////////

#include "QComboBox.h"

void FreeComboBox(void *pVoid)
{
	delete (CQComboBox*)pVoid;
}

VALUE HandleComboBox(CQComboBox *pQ, int event, VALUE arg)
{
	VALUE vResult, self = pQ->GetSelf();
	switch (event) {
		case QW_DESTROY:
			self.Set(_T("parent"),AllocNil());
			self.Set(_T("#pQ"),AllocNil());
			break;
		case QW_DOUBLECLICK:
			vResult = ExecMethod(self, _T("onDoubleClick"));
			break;
		case QW_SELCHANGE:
			vResult = ExecMethod(self, _T("onSelChange"));
			break;
		case QW_FOCUS:
			vResult = ExecMethod(self, _T("onFocus"));
			break;
		case QW_UNFOCUS:
			vResult = ExecMethod(self, _T("onUnfocus"));
			break;
	}
	return vResult;
}

CORE_FUNC(ComboBox__Init__)
{
	// gui.ComboBox(parent, rect, <optionsFrame>)
	ASSERT_SELF(GUI_MODULE_NAME);
	if ((argCount != 2) && (argCount != 3)) {
		ERR("gui.ComboBox contructor requires 2 or 3 arguments");
	}
	CQWindow* pQWindow = ValueToWindow(args[0], pInterp); if (!pQWindow) return VALUE();
	CRect rc;
	if (ValueToRect(args[1], pInterp, rc)) {
		int options = QCBS_BORDER | QCBS_DROPDOWNLIST;	// Default options
		if ((argCount == 3) && (args[2].Type() == stFrame)) {
			VALUE v = args[2][_T("edit")];
			if ((v.Type() != stUndefined) && v.Boolean()) {
				options = QCBS_BORDER | QCBS_DROPDOWN;
			}
			ExtractOption(args[2], _T("border"), QCBS_BORDER, options);
			ExtractOption(args[2], _T("sort"), QCBS_SORT, options);
		}
		CQComboBox* pQ = new CQComboBox(pQWindow, rc, options, HandleComboBox, *self);
		self->Set(_T("#pQ"),AllocVoid(pQ, FreeComboBox, _T("combobox")));
		self->Set(_T("parent"), AllocProperty(
			AllocFunction(Generic_ReadParent, GUI_MODULE_NAME),
			AllocFunction(Cant_WriteMe, GUI_MODULE_NAME),
			(int)args[0].ClassInstance()));
	}
	return VALUE();
}

CORE_FUNC(ComboBox_AddString) //(self,argCount,args[],pInterp)
{
	BEGIN_METHOD(CQComboBox);
	REQUIRE_ARGS(1);
	return pQ->AddString(args[0].String()) + 1;
}

CORE_FUNC(ComboBox_DelString) //(self,argCount,args[],pInterp)
{
	BEGIN_METHOD(CQComboBox);
	REQUIRE_ARGS(1);
	REQUIRE_INTEGER(0);
	return pQ->DeleteString(args[0].Integer() - 1);
}

CORE_FUNC(ComboBox_Count) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(0);
	BEGIN_METHOD(CQComboBox);
	return pQ->GetCount();
}

CORE_FUNC(ComboBox_ReadSelect) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CQComboBox);
	return pQ->GetCurSel() + 1;
}

CORE_FUNC(ComboBox_WriteSelect) //(self,argCount,args[],pInterp)
{
	REQUIRE_ARGS(2);	// Write prop always has one
	BEGIN_METHOD(CQComboBox);

	if (args[1].Type() == stNil) {
		pQ->SetCurSel(-1);
	}
	else if (args[1].Type() == stInteger) {
		pQ->SetCurSel(args[1].Integer() - 1);
	}
	else ERR("Invalid arg type assigned to ComboBox.select property");

	return AllocNil();
}

CORE_FUNC(ComboBox_ReadContents)
{
	REQUIRE_ARGS(1);	// Read prop always has one
	BEGIN_METHOD(CQComboBox);

	int length = pQ->GetCount();
	VALUE vArray = AllocArray(length);
	for (int idx = 0; idx < length; ++idx) {
		CString text;
		pQ->GetLBText(idx, text);
		vArray.Set(idx + 1, text);
	}
	return vArray;
}

CORE_FUNC(ComboBox_WriteContents)
{
	REQUIRE_ARGS(2);	// Write prop always has one
	BEGIN_METHOD(CQComboBox);

	VALUE vData = args[1];

	if (vData.Type() == stNil) {
		pQ->ResetContent();
	}
	else if (vData.Type() == stString) {
		pQ->ResetContent();
		pQ->AddString(vData.String());
	}
	else if (vData.Type() == stArray) {
		pQ->ResetContent();
		int length = vData.Length();
		for (int idx = 0; idx < length; ++idx) {
			pQ->InsertString(idx, vData[idx + 1].String());
		}
	}
	else ERR("Invalid argument type to ComboBox.content write property");

	return AllocNil();
}

VALUE ComboBoxClass()
{
	VALUE ComboBoxClass = AllocClass(_T("combobox"), VALUE());
	ComboBoxClass.Set(_T("__init__"),AllocFunction(ComboBox__Init__, GUI_MODULE_NAME));
	ComboBoxClass.Set(_T("add"),AllocFunction(ComboBox_AddString, GUI_MODULE_NAME));
	ComboBoxClass.Set(_T("delete"),AllocFunction(ComboBox_DelString, GUI_MODULE_NAME));
	ComboBoxClass.Set(_T("count"),AllocFunction(ComboBox_Count, GUI_MODULE_NAME));
	ComboBoxClass.Set(_T("contents"), AllocProperty(
		AllocFunction(ComboBox_ReadContents, GUI_MODULE_NAME),
		AllocFunction(ComboBox_WriteContents, GUI_MODULE_NAME),
		AllocNil()));
	ComboBoxClass.Set(_T("selection"), AllocProperty(
		AllocFunction(ComboBox_ReadSelect, GUI_MODULE_NAME),
		AllocFunction(ComboBox_WriteSelect, GUI_MODULE_NAME),
		AllocNil()));
	ComboBoxClass.Set(_T("visible"), vVisibleProp);
	ComboBoxClass.Set(_T("enabled"), vEnabledProp);
	ComboBoxClass.Set(_T("focused"), vFocusedProp);
	ComboBoxClass.Set(_T("rect"), vRectProp);
	ComboBoxClass.Set(_T("text"), vTextProp);
	return ComboBoxClass;
}

//////////////////////////////////////////////////////////////////////
// Dialect GUI Module Methods
//////////////////////////////////////////////////////////////////////

bool PumpMessages(CInterpreter* pInterp)
{
	// Return false on WM_QUIT, true all other times (when no messages left in queue)
#ifndef UNDER_CE
    //MSG msg;
	// Peek message was freezing on my HP-360LX!
	//if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
#endif
        //if (!AfxGetApp()->PumpMessage()) {
		if (!AppPumpMessageReplacement()) {
			pInterp->Abort();
            ::PostQuitMessage(0);
			return false;
		}
#ifndef UNDER_CE
	//}
#endif
    // let MFC do its idle processing
	// NOTE - This is where the MFC frame work checks and updates
	// the enable status of the toolbar buttons...
	LONG lIdle = 0;
    while (AfxGetApp()->OnIdle(lIdle++));
	return true;
}

CORE_FUNC(GUI_Pump)
{
	if (argCount > 1) {
		ERR("Incorrect number of args to gui.pump()");
	}

	if ((argCount == 1) && args[0].Boolean()){
		MSG msg;
		while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
			//if (!AfxGetApp()->PumpMessage()) {
			if (!AppPumpMessageReplacement()) {
				pInterp->Abort();
				::PostQuitMessage(0);
				return AllocNil();
			}
		}
	}
	else {
		//if (!AfxGetApp()->PumpMessage()) {
		if (!AppPumpMessageReplacement()) {
			pInterp->Abort();
			::PostQuitMessage(0);
			return AllocNil();
		}
	}
    // let MFC do its idle processing
	// NOTE - This is where the MFC frame work checks and updates
	// the enable status of the toolbar buttons...
	LONG lIdle = 0;
    while (AfxGetApp()->OnIdle(lIdle++));
	return AllocNil();
}

bool GUI_quit = false;
CTime t;

CORE_FUNC(GUI_Enter)
{
	static int start = t.GetSecond();
	GUI_quit = false;

	static_cast<CDialectInterp*>(pInterp)->SetBackgroundHandler(0, NULL);

	while ((!GUI_quit) && (NumberOfOpenWindows > 0))
	{
		PumpMessages(pInterp);
		int flag = pInterp->GetReturnFlag();
		if ((flag != RETURN_NOT) && (flag != RETURN_FUNCTION)) {
			return AllocNil();
		}

		//if ((*self)[_T("idle")].Type() == stFunction) {
		//
		//}
	}
	return AllocNil();
}

CORE_FUNC(GUI_Exit) //(self,argCount,args[],pInterp)
{
	GUI_quit = true;
	return AllocNil();
}

CORE_FUNC(GUI_ScreenRect)
{
	// Return the usable screen rect (ie. minus task bar)
	RECT rc;
	SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
	VALUE vArray = AllocArray(4);
	vArray.Set(1, rc.left);
	vArray.Set(2, rc.top);
	vArray.Set(3, rc.right);
	vArray.Set(4, rc.bottom);
	return vArray;
}

CORE_FUNC(GUI_MousePos)
{
	POINT pt;
#ifdef UNDER_CE
	pt.x = _mouseX;
	pt.y = _mouseY;
#else
	GetCursorPos(&pt);
#endif

	if ((argCount > 0) && (args[0].Type() == stClassInstance)) {
		VALUE v = args[0][_T("#pQ")];
		if (v.Type() == stVoid) {
			CWnd* pWnd = (CWnd*)v.Void();
			if (pWnd != NULL) {
				pWnd->ScreenToClient(&pt);
			}
		}
	}

	VALUE vArray = AllocArray(2);
	vArray.Set(1, pt.x);
	vArray.Set(2, pt.y);
	return vArray;
}

static void SplitFullPath(CString input, CString& directory, CString& filename)
{
	directory.Empty();
	filename.Empty();

	if (!input.IsEmpty()) {
		int pos = input.ReverseFind('\\');
		if (pos >= 0) {
			filename = input.Mid(pos + 1);
			directory = input.Left(pos + 1);	// Leave slash
		}
		else {
			filename = input;
		}
	}
}

CORE_FUNC(GUI_FileOpenDlg)
{
	REQUIRE_ARGS(4);
	REQUIRE_STRING(1);
	REQUIRE_STRING(2);
	REQUIRE_STRING(3);

	CWnd* pParent = NULL;
	if (args[0].Type() == stClassInstance) {
		pParent = (CWnd*)args[0][_T("#pQ")].Void();
		if (pParent == NULL) {
			ERR("First argument must be nil or parent window object");
		}
	}

	CString initialFile = args[1].String();		// "*.d" or "myFile.d"
	CString defExt = args[2].String();			// ".d"
	defExt.TrimLeft();
	if (defExt[0] == '.') {
		defExt = defExt.Mid(1);
	}
	CString fileTypes = args[3].String();		// "Dialect Files (*.d)|*.d||"
	for (int idx = 0; idx < fileTypes.GetLength(); ++idx) {
		if (fileTypes[idx] == '|') fileTypes.SetAt(idx, '\0');
	}

	TCHAR _FullPath[MAX_PATH];
	memset(_FullPath, 0, MAX_PATH * sizeof(TCHAR));
	if (!initialFile.IsEmpty()) _tcscpy(_FullPath, (LPCTSTR)initialFile);

	OPENFILENAME ofn;
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = pParent ? pParent->GetSafeHwnd() : NULL;
	ofn.hInstance = AfxGetInstanceHandle();
	ofn.lpstrFilter = fileTypes;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = _FullPath;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = defExt;

	VALUE vArray = AllocArray(2, AllocNil());
	if (GetOpenFileName(&ofn)) {
		CString fullPath = _FullPath;//dlg.GetPathName();
		CString directory, filename;
		SplitFullPath(fullPath, directory, filename);

		vArray.Set(1, directory);
		vArray.Set(2, filename);
	}
	return vArray;
}

CORE_FUNC(GUI_FileSaveAsDlg)
{
	// The CE file dialog is a bit incomplete, etc.
	// For example, CFileDialog.GetFileName() doesn't work in CE...

	REQUIRE_ARGS(4);
	REQUIRE_STRING(1);
	REQUIRE_STRING(2);
	REQUIRE_STRING(3);

	CWnd* pParent = NULL;
	if (args[0].Type() == stClassInstance) {
		pParent = (CWnd*)args[0][_T("#pQ")].Void();
		if (pParent == NULL) {
			ERR("First argument must be nil or parent window object");
		}
	}

	CString initialFile = args[1].String();		// "*.d" or "myFile.d"
	CString defExt = args[2].String();			// "d"
	defExt.TrimLeft();
	if (defExt[0] == '.') {
		defExt = defExt.Mid(1);
	}
	CString fileTypes = args[3].String();		// "Dialect Files (*.d)|*.d||"
	for (int idx = 0; idx < fileTypes.GetLength(); ++idx) {
		if (fileTypes[idx] == '|') fileTypes.SetAt(idx, '\0');
	}

	TCHAR _FullPath[MAX_PATH];
	memset(_FullPath, 0, MAX_PATH * sizeof(TCHAR));
	if (!initialFile.IsEmpty()) _tcscpy(_FullPath, (LPCTSTR)initialFile);

	OPENFILENAME ofn;
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = pParent ? pParent->GetSafeHwnd() : NULL;
	ofn.hInstance = AfxGetInstanceHandle();
	ofn.lpstrFilter = fileTypes;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = _FullPath;
	ofn.nMaxFile = MAX_PATH;
#ifdef _WIN32_WCE_PSPC
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
#else
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_NOREADONLYRETURN | OFN_HIDEREADONLY;
#endif
	ofn.lpstrDefExt = defExt;

	VALUE vArray = AllocArray(2, AllocNil());
	if (GetSaveFileName(&ofn)) {
		CString fullPath = _FullPath;
		CString directory, filename;
		SplitFullPath(fullPath, directory, filename);

#ifdef _WIN32_WCE_PSPC
		// Something strange happening
		if (directory.Left(2) == _T("\\\\"))
			directory = directory.Mid(1);
		if (filename[filename.GetLength() - 1] == '.')
			filename = filename.Left(filename.GetLength() - 1);
#endif

		vArray.Set(1, directory);
		vArray.Set(2, filename);
	}
	return vArray;
}

CORE_FUNC(GUI_MessageBox)
{
	REQUIRE_ARGS(4);
	REQUIRE_INTEGER(3);

	CWnd* pParent = NULL;
	if (args[0].Type() == stClassInstance) {
		pParent = (CWnd*)args[0][_T("#pQ")].Void();
		if (pParent == NULL) {
			ERR("First argument must be nil or parent window object");
		}
	}
	else {
		pParent = AfxGetMainWnd();
		if (!::IsWindow(pParent->GetSafeHwnd())) {
			pParent = NULL;
		}
	}

	//if (pParent) {
		return (int)MessageBox(pParent ? pParent->GetSafeHwnd() : NULL,
			args[1].String(), args[2].String(), args[3].Integer());
	//}
	//else {
	//	return (int)AfxMessageBox(args[1].String(), args[3].Integer());
	//}
}

//////////////////////////////////////////////////////////////////////
// Popup menu
//////////////////////////////////////////////////////////////////////

/*static CMenu PopupMenu;

CORE_FUNC(GUI_PopupMenu)
{
	REQUIRE_ARGS(4);

	VALUE vParent = args[0];
	if (vParent.Type() != stClassInstance) {
		ERR("Need a window class instance as first arg in gui.popupMenu()");
	}
	CWnd* pWnd = (CWnd*)vParent.Get(_T("#pQ")).Void();
	if (pWnd == NULL) {
		ERR("Need a window class instance as first arg in gui.popupMenu()");
	}

	// Menu location on screen...
	REQUIRE_INTEGER(1);
	REQUIRE_INTEGER(2);
	POINT pt;
	pt.x = args[1].Integer();
	pt.y = args[2].Integer();
	pWnd->ClientToScreen(&pt);


	REQUIRE_ARRAY(3);
	VALUE theList = args[3];
	int len = theList.Length();

	// We will invoke a parents OnMenuSelect function, give them the index
	// and they can find the item by:  __lastmenu__[idx]
	// Note, this also provides a good way to maintian the list until
	// garbage collection is allowed
	vParent.Set(_T("#lastmenu"), theList);

	PopupMenu.DestroyMenu();
	PopupMenu.CreatePopupMenu();

	VALUE theEntry;
	float count = 0.0;
	for (int k = 1; k <= len; k++)
	{
		DWORD mb = 0;
#ifdef UNDER_CE
		if (count>=11.5)
		{
			mb = MF_MENUBARBREAK;
			count = 0.0;
		}
#endif
		UINT options = 0;
		theEntry = theList[k];	// theList -> [["one", 1], ["two", 2, {enabled:false}]]
		if ((theEntry.Type() == stArray) && (theEntry.Length() > 0)) {
			if (theEntry.Length() == 3) {
				VALUE v = theEntry[3][_T("enabled")];
				if (v.Type() != stUndefined) {
					if (v.Boolean()) options |= MF_ENABLED;
					else options |= MF_GRAYED;
				}
				v = theEntry[3][_T("checked")];
				if (v.Type() != stUndefined) {
					if (v.Boolean()) options |= MF_CHECKED;
				}
			}
			theEntry = theEntry[1];	// Just want the actual menu item text
		}

		if (theEntry.Type() == stString)
		{
			CString entry = theEntry.String();
			if (entry==_T("-")) {
				::AppendMenu(PopupMenu.m_hMenu, mb | MF_SEPARATOR,0,_T(""));
				count += 0.5;
			}
			else {
				::AppendMenu(PopupMenu.m_hMenu, mb | MF_STRING | options,1000+k,entry);
				count += 1.0;
			}
		}
	}

#ifdef UNDER_CE
	PopupMenu.TrackPopupMenu(TPM_LEFTALIGN, pt.x, pt.y, pWnd);
#else
	PopupMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, pWnd);
#endif

	return AllocNil();
}*/

#define MAX_MENU_COUNT	10
static CMenu PopupMenus[MAX_MENU_COUNT];
static int MenuCount = 0;
static VALUE vMenuIDToValue;

static UINT MakePopupMenu(VALUE vArray, VALUE vIDToValue, CInterpreter* pInterp)
{
	if (MenuCount >= MAX_MENU_COUNT) {
		pInterp->RuntimeError(_T("Too many sub-menus"));
		return 0;
	}
	else if (vArray.Type() != stArray) {
		pInterp->RuntimeError(_T("Invalid menu structure"));
		return 0;
	}

	int menuIdx = MenuCount; // The static
	MenuCount++;
	int len = vArray.Length();

	PopupMenus[menuIdx].DestroyMenu(); // To be safe
	PopupMenus[menuIdx].CreatePopupMenu();

	VALUE theEntry;
	float count = 0.0;
	for (int k = 1; k <= len; k++) {
		DWORD mb = 0;
#ifdef UNDER_CE
		if (count>=11.5) {
			mb = MF_MENUBARBREAK;
			count = 0.0;
		}
#endif
		UINT hSubMenu = 0;
		UINT options = 0;
		int id = k + 500 * menuIdx;
		theEntry = vArray[k];	// theList -> [["one", 1], ["two", 2, {enabled:false}]]
		if ((theEntry.Type() == stArray) && (theEntry.Length() > 0)) {
			if (theEntry.Length() >= 2) {
				if (theEntry[2].Type() == stArray) {
					// This is a submenu item, create a new menu
					id = MakePopupMenu(theEntry[2], vIDToValue, pInterp);
					if (!id) return 0;
					options |= MF_POPUP;
				}
				else {
					// Use the item as the menu result
					CString temp;
					temp.Format(_T("%d"), id);
					vMenuIDToValue.Set(temp, theEntry[2]);
				}
			}

			if (theEntry.Length() == 3) {
				VALUE v = theEntry[3][_T("enabled")];
				if (v.Type() != stUndefined) {
					if (v.Boolean()) options |= MF_ENABLED;
					else options |= MF_GRAYED;
				}
				v = theEntry[3][_T("checked")];
				if (v.Type() != stUndefined) {
					if (v.Boolean()) options |= MF_CHECKED;
				}
			}

			theEntry = theEntry[1];	// Just want the actual menu item string
		}
		else {
			// Use the item as the menu result
			CString temp;
			temp.Format(_T("%d"), id);
			vMenuIDToValue.Set(temp, theEntry);
		}

		CString entry = theEntry.String();
		if (entry == _T("-")) {
			::AppendMenu(PopupMenus[menuIdx].m_hMenu, mb | MF_SEPARATOR, 0, _T(""));
			count += 0.5;
		}
		else {
			::AppendMenu(PopupMenus[menuIdx].m_hMenu, mb | MF_STRING | options, id, entry);
			count += 1.0;
		}
	}
	return (UINT)PopupMenus[menuIdx].m_hMenu;
}

CORE_FUNC(GUI_PopupMenu)
{
	REQUIRE_ARGS(4);

	CQWindow* pWnd = NULL;
	CQPane* pWndPane = NULL; // Should be derived from CQWindow!
	VALUE vParent = args[0];
	if (vParent.Type() == stClassInstance) {
		if (vParent.Isa(_T("window"))) {
			pWnd = (CQWindow*)vParent.Get(_T("#pQ")).Void();
		}
		else if (vParent.Isa(_T("pane"))) {
			pWndPane = (CQPane*)vParent.Get(_T("#pQ")).Void();
		}
	}

	if (!pWnd && !pWndPane) {
		ERR("Need a window or pane class instance as first arg in gui.popupMenu()");
	}

	CWnd* p = (CWnd*)pWnd ? (CWnd*)pWnd : (CWnd*)pWndPane;

	// Menu location on screen...
	REQUIRE_INTEGER(1);
	REQUIRE_INTEGER(2);
	POINT pt;
	pt.x = args[1].Integer();
	pt.y = args[2].Integer();
	p->ClientToScreen(&pt);

	MenuCount = 0;
	vMenuIDToValue = AllocFrame();

	VALUE vResult = AllocNil();
	if (MakePopupMenu(args[3], vMenuIDToValue, pInterp)) {
		#ifdef UNDER_CE
			DWORD dwResult = PopupMenus[0].TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, pt.x, pt.y, p);
		#else
			DWORD dwResult = PopupMenus[0].TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, pt.x, pt.y, p);
		#endif

			if (dwResult != 0) {
				CString temp;
				temp.Format(_T("%d"), dwResult);
				vResult = vMenuIDToValue.Get(temp);
				if (pWnd && pWnd->m_pHandler) {
					// Call the handler here (no longer using WM_COMMAND)
					pWnd->m_pHandler(pWnd, QW_MENUSELECT, vResult);
				}
				else if (pWndPane && pWndPane->m_pHandler) {
					// Call the handler here (no longer using WM_COMMAND)
					pWndPane->m_pHandler(pWndPane, QW_MENUSELECT, vResult);
				}
			}
			vMenuIDToValue = AllocNil();
	}
	for (int idx = MenuCount - 1; idx >= 0; --idx) {
		PopupMenus[idx].DestroyMenu();
	}

	return vResult;
}

//////////////////////////////////////////////////////////////////////

static void FreeWaitCursor(void *pVoid)
{
	delete (CWaitCursor*)pVoid;
}

CORE_FUNC(GUI_WaitCursor)
{
	return AllocVoid(new CWaitCursor(), FreeWaitCursor, _T("waitcursor"));
}

#if defined(WIN32_PLATFORM_PSPC) && (UNDER_CE >= 300)
#include <aygshell.h>
CORE_FUNC(GUI_SHFullScreen)
{
	if (argCount < 1) {
		ERR("SHFullScreen requires at least one arg.");
	}
	HWND hwnd = (HWND)args[0].Integer();
	DWORD state = 4; // SHFS_SHOWSIPBUTTON
	if (argCount == 2) {
		state = args[1].Integer();
	}
	return (bool)(SHFullScreen(hwnd, state) == TRUE);
}

/*CORE_FUNC(GUI_ShowSip)
{
	if (argCount < 1) {
		ERR("ShowSip requires at least one arg.");
	}
}*/
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

VALUE InitGUIModule()
{
	VALUE module = AllocFrame();
	module.Set(_T("__module__"), GUI_MODULE_NAME);

	module.Set(_T("MessageBox"), AllocFunction(GUI_MessageBox, GUI_MODULE_NAME)); // Delete me
	module.Set(_T("MsgBox"), AllocFunction(GUI_MessageBox, GUI_MODULE_NAME));
	module.Set(_T("mbOK"), (int)MB_OK);
	module.Set(_T("mbOKCancel"), (int)MB_OKCANCEL);
	module.Set(_T("mbRetryCancel"), (int)MB_RETRYCANCEL);
	module.Set(_T("mbYesNo"), (int)MB_YESNO);
	module.Set(_T("mbYesNoCancel"), (int)MB_YESNOCANCEL);
	module.Set(_T("mbIconExclamation"), (int)MB_ICONEXCLAMATION);
	module.Set(_T("mbIconInformation"), (int)MB_ICONINFORMATION);
	module.Set(_T("mbIconQuestion"), (int)MB_ICONQUESTION);
	module.Set(_T("mbIconStop"), (int)MB_ICONSTOP);
	module.Set(_T("ok"), (int)IDOK);
	module.Set(_T("yes"), (int)IDYES);
	module.Set(_T("no"), (int)IDNO);
	module.Set(_T("retry"), (int)IDRETRY);
	module.Set(_T("cancel"), (int)IDCANCEL);

	module.Set(_T("pump"), AllocFunction(GUI_Pump, GUI_MODULE_NAME));
	module.Set(_T("Enter"), AllocFunction(GUI_Enter, GUI_MODULE_NAME));
	module.Set(_T("Exit"),  AllocFunction(GUI_Exit, GUI_MODULE_NAME));
	module.Set(_T("ScreenRect"),  AllocFunction(GUI_ScreenRect, GUI_MODULE_NAME));
	module.Set(_T("mousePos"),  AllocFunction(GUI_MousePos, GUI_MODULE_NAME));
	module.Set(_T("FileOpenDlg"),  AllocFunction(GUI_FileOpenDlg, GUI_MODULE_NAME));
	module.Set(_T("FileSaveAsDlg"),  AllocFunction(GUI_FileSaveAsDlg, GUI_MODULE_NAME));
	module.Set(_T("popupMenu"),  AllocFunction(GUI_PopupMenu, GUI_MODULE_NAME));
	module.Set(_T("window"), WindowClass());
	module.Set(_T("menubar"), MenuBarClass());
	module.Set(_T("button"), ButtonClass());
	module.Set(_T("checkbox"), CheckBoxClass());
	module.Set(_T("radio"), RadioButtonClass());
	module.Set(_T("pane"), PaneClass());
	module.Set(_T("edit"), EditClass());
	module.Set(_T("note"), NoteClass());
	module.Set(_T("grid"), GridClass());
	module.Set(_T("label"), LabelClass());
	module.Set(_T("listbox"), ListBoxClass());
	module.Set(_T("combobox"), ComboBoxClass());
	module.Set(_T("scrollbar"), ScrollBarClass());
#if defined(DIALECT_ENT)
	module.Set(_T("layout"), LayoutClass());
	module.Set(_T("ink"), InkClass());
#endif
	module.Set(_T("calendar"), CalendarClass());
	module.Set(_T("bitmap"), BitmapClass());
	module.Set(_T("waitcursor"), AllocFunction(GUI_WaitCursor, GUI_MODULE_NAME));

#if defined(WIN32_PLATFORM_PSPC) && (UNDER_CE >= 300)
	module.Set(_T("SHFullScreen"), AllocFunction(GUI_SHFullScreen, GUI_MODULE_NAME));
	//module.Set(_T("ShowSip"), AllocFunction(GUI_ShowSip, GUI_MODULE_NAME));
#endif

	return module;
}
